导航:[首页]->[bash]->[Bash学习-陷阱]

From idv2

for i in ls *.mp3

若文件名存在空格,将导致一个文件变成两个来解析,正确的用法是 for var in *;do echo ":$var" done

cp $file $target

同样存在空格问题 cp "${file}" "${target}"

[ $foo = "bar" ]

大部分情况下,这工作得很好,但是若$foo为空,会导致语法错误 [ "null${foo}" = "nullbar" ]

cd dirname "$f"

同样存在空格问题 cd "dirname "$f"" $()语法也相同,如下面的写法是正确的。 cd "$(dirname "$f")"

[ $foo -gt 7 ]

当 $foo 不是数字时就会出错。你必须做好类型检验。 [[ $foo -gt 7 ]]

grep foo bar | while read line; do ((count++)); done

管道中的每个命令都放到一个新的子shell中执行, 所以子shell中定义的count变量无法传递出来。 grep foo bar | wc -l

if [grep foo myfile]

初学者常犯的错误,就是将 if 语句后面的 [ 当作if语法的一部分。实际上它是一个命令,相当于 test 命令, 而不是 if 语法。这一点C程序员特别应当注意。

if [bar="$foo"]

同样,[ 是个命令,不是 if 语句的一部分,所以要注意空格

if [ [ a = b ] && [ c = d ] ]

同样的问题,[ 不是 if 语句的一部分,当然也不是改变逻辑判断的括号。它是一个命令。可能C程序员比较容易犯这个错误? if [ a = b ] && [ c = d ]

cat file | sed s/foo/bar/ > file

你不能在同一条管道操作中同时读写一个文件。根据管道的实现方式,file要么被截断成0字节,要么会无限增长直到填满整个硬盘。如果想改变原文件的内容,只能先将输出写到临时文件中再用mv命令。 sed 's/foo/bar/g' file > tmpfile && mv tmpfile file

echo $foo

这句话还有什么错误码?一般来说是正确的,但下面的例子就有问题了。 MSG="Please enter a file name of the form *.zip" echo $MSG # 错误! 如果恰巧当前目录下有zip文件,就会显示成 Please enter a file name of the form freenfss.zip lw35nfss.zip 所以即使是echo也别忘记给变量加引号。

echo <<EOF

here document是个好东西,它可以输出成段的文字而不用加引号也不用考虑换行符的处理问题。 不过here document输出时应当使用cat而不是echo。 # This is wrong: echo <<EOF Hello world EOF

    # This is right:
    cat <<EOF
    Hello world
    EOF

cd /foo; bar

cd有可能会出错,出错后 bar 命令就会在你预想不到的目录里执行了。所以一定要记得判断cd的返回值。

关于目录的一点题外话,假设你要在shell程序中频繁变换工作目录,如下面的代码: find ... -type d | while read subdir; do cd "$subdir" && whatever && ... && cd - done 不如这样写: find ... -type d | while read subdir; do (cd "$subdir" && whatever && ...) done 括号会强制启动一个子shell,这样在这个子shell中改变工作目录不会影响父shell(执行这个脚本的shell), 就可以省掉cd - 的麻烦。

你也可以灵活运用 pushd、popd、dirs 等命令来控制工作目录。

[ bar == "$foo" ]

[ 命令中不能用 ==,应当写成 [ bar = "$foo" ] && echo yes [[ bar == $foo ]] && echo yes

UTF-8的BOM(Byte-Order Marks)问题

UTF-8编码可以在文件开头用几个字节来表示编码的字节顺序,这几个字节称为BOM。但Unix格式的UTF-8编码不需要BOM。 多余的BOM会影响shell解析,特别是开头的 #!/bin/sh 之类的指令将会无法识别

MS-DOS格式的换行符(CRLF)也存在同样的问题。如果你将shell程序保存成DOS格式,脚本就无法执行了。

for arg in $*

$*表示所有命令行参数,所以你可能想这样写来逐个处理参数,但参数中包含空格时就会失败。

正确的方法是使用 $@。 * Expands to the positional parameters, starting from one.
When the expansion occurs within double quotes, it expands to a single word with the value of each parameter separated by the first character of the IFS special variable.
That is, "$*" is equivalent to "$1c$2c...", @ Expands to the positional parameters, starting from one. When the expansion occurs within double quotes, each parameter expands to a separate word. That is, "$@"
is equivalent to "$1" "$2" ...

function foo()

在bash中没有问题,但其他shell中有可能出错。不要把 function 和括号一起使用。 最为保险的做法是使用括号,即 foo() { ... }

参考