单引号和双引号
str='this is a str'
- 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的
- 单引号字串中不能出现单引号(对单引号使用转义符后也不行)
hidden='A Deep Web'
str="Hello, I know it is \"$your_name\"! \n"
- 双引号里可以有变量
- 双引号里可以出现转义字符
for i in $(ls *.mp3)
常见的错误代码如下:
for i in $(ls *.mp3); do # Wrong!
some command $i # Wrong!
done
for i in $(ls) # Wrong!
for i in `ls` # Wrong!
for i in $(find . -type f) # Wrong!
for i in `find . -type f` # Wrong!
for i in "$(ls *.mp3)"; do # Wrong!
如果文件中有空格或者通配符的话,就会出现问题。一种较好的形式是:
for i in $somedir/*.mp3; do
# 第三行代码的作用是万一文件不存在不会出现"$somedir/*.mp3"
[[ -f "$i" ]] || continue
some command "$i"
done
cp $file $target
最好写成:cp "$file" "$target"
因为文件中有空格(space)或者通配符(wildcards)就有问题了。
[ $foo = "bar" ]
更好的写法是:
[[ $foo = "bar" ]]
或者 [[ $foo == "bar" ]]
不要使用[[ $foo > 7 ]]进行比较
If you just want to do a numeric comparison (or any other shell arithmetic), it is much better to just use (( )) instead:
# Bash / Ksh
((foo > 7)) # Right!
[[ "$foo" -gt 7 ]] # Works, but is pointless. Most will consider it wrong. Use ((...)) or let instead.
if [grep foo myfile]
其实[]
是test命令,但是很多人认为if后面的命令就是这么写的。
真正的if语法是这样的:
if COMMANDS; then
COMMANDS
elif COMMANDS; then
COMMANDS
else
COMMANDS
fi
所以使用方式是:
# 你可以这么用,使用test语法[[ somecode ]]
if [[ -n "${my_var}" ]]; then
do_something
fi
# 你也可以使用一般的命令
if grep foo myfile >/dev/null 2>&1; then
some commands
fi
If the grep matches a line from myfile, then the exit code will be 0 (true), and the then part will be executed. Otherwise, if there is no matching line, the grep should return a non-zero exit code.
关于read命令
#!/usr/bin/env bash
# Above line is so called shebang
# IFS的默认值为空白符(换行符, 制表符或者空格)
# 如果是其它分隔符,要设置IFS以读取字段
OldIFS=$IFS
IFS=','
while read column1 column2 column3; do
echo "$column1"
echo "$column2"
echo "$column3"
done < test
IFS=${OldIFS}
IFS=- read -ra parts <<< "foo-bar-baz"
echo ${parts[0]} # foo
echo ${parts[2]} # baz
echo ${#parts} # 3
echo ${#parts[@]} # 3
echo ${parts[@]} # foo bar baz
<<<
means here string
-a
option告诉read命令将分割后的元素保存到parts数组中
更改文件的内容
# Don't do this
cat file | sed s/foo/bar/ > file # Wrong!
# The following is completely portable
sed 's/foo/bar/g' file > tmpfile && mv tmpfile file # Good
# If changed string have space, you should quote it
sed "s/foo/bar other/g" file > tmpfile && mv tmpfile file
# Or you can do it with perl 5.x, file(s) means you can
#+ add one more files
perl -pi -e 's/foo/bar/g' file(s) # Also good too
小心使用echo $msg
$ ls *.awk
histans1.awk test.awk
$ msg="Please enter a file name of the form *.awk"
$ echo $msg
Please enter a file name of the form histans1.awk test.awk
$ echo "$msg" # Right
Please enter a file name of the form *.awk
所以你在使用echo输出变量信息时要注意加上引号,另一种安全的写法是:
printf "%s\n" "$msg"
少使用for arg in $*
尽量使用for arg in "$@"
参考$* and $@ in bash
不要这样使用somecmd 2>&1 >logfile
Use: somecmd >logfile 2>&1
instead
Redirections are evaluated left-to-right before the command is executed.
所以你知道为什么sed 's/foo/bar/' file > file
不对了吧?
顺便说明一下linux下默认的文件描述符
stdin(标准输入) - 0
stdout(标准输出) - 1
stderr(标准错误) - 2
Use of Shell Builtin Commands
If possible shell buitins should be preferred to external utilities. Each call of sed, awk, cut etc. generates a new process. Used in a loop this can extend the execution time considerably. In the following example the shell parameter expansion is used to get the base name and the directory of a path:
for pathname in $(find $search - type f -name "*"); do
basename=${pathname##*/} # replaces basename(1)
dirname=${pathname%/*} # replaces dirname(1)
done
name="File.sh"
echo "${name:-other}" # File.sh
echo "${nonvar:-incorrect}" # 变量nonvar不存在, 输出incorrect
echo "name变量的长度: ${#name}" # 7
echo "${name: -2}" # 取变量的最后两个字符(sh), 注意:和-之间的空格