Bash的24个陷阱分析

2017-09-30

bash编程常见错误。

1. for i in `ls *.mp3`

常见的错误写法:
for i in `ls *.mp3`; do # Wrong!

为 什么错误呢?因为for...in语句是按照空白来分词的,包含空格的文件名会被拆成多个词。如遇到 01 - Don't Eat the Yellow Snow.mp3 时,i的值会依次取 01,-,Don't,等等。

用双引号也不行,它会将ls *.mp3的全部结果当成一个词来处理。
for i in "`ls *.mp3`"; do # Wrong!

正 确的写法是
for i in *.mp3; do

2. cp $file $target

这句话 基本上正确,但同样有空格分词的问题。所以应当用双引号:
cp "$file" "$target"

但是如果凑巧文 件名以 - 开头,这个文件名会被 cp 当作命令行选项来处理,依旧很头疼。可以试试下面这个。
cp -- "$file" "$target"

运 气差点的再碰上一个不支持 -- 选项的系统,那只能用下面的方法了:使每个变量都以目录开头。
for i in ./*.mp3; do cp "$i" /target ...

3. [ $foo = "bar" ]

当$foo为空时,上面的命令就变成了
[ = "bar" ]

类 似地,当$foo包含空格时:
[ multiple words here = "bar" ]

两者都会出错。所以 应当用双引号将变量括起来:
[ "$foo" = bar ] # 几乎完美了。

但是!当$foo以 - 开头时依然会有问题。在较新的bash中你可以用下面的方法来代替,[[ 关键字能正确处理空白、空格、带横线等问题。
[[ $foo = bar ]] # 正确

旧 版本bash中可以用这个技巧(虽然不好理解):
[ x"$foo" = xbar ] # 正确

或者干脆把 变量放在右边,因为 [ 命令的等号右边即使是空白或是横线开头,依然能正常工作。(Java编程风格中也有类似的做法,虽然目的不一样。)
[ bar = "$foo" ] # 正确

4. cd `dirname "$f"`

同样也存在空格问题。那么加上引号吧。
cd "`dirname "$f"`"

问 题来了,是不是写错了?由于双引号的嵌套,你会认为`dirname 是第一个字符串,`是第二个字符串。错了,那是C语言。在bash中,命令替换(反引号``中的内容)里面的双引号会被正确地匹配到一起,不用特意去转 义。

$()语法也相同,如下面的写法是正确的。
cd "$(dirname "$f")"

5. [ "$foo" = bar && "$bar" = foo ]

[ 中不能使用 && 符号!因为 [ 的实质是 test 命令,&& 会把这一行分成两个命令的。应该用以下的写法。
[ bar = "$foo" -a foo = "$bar" ] # Right![ bar = "$foo" ] && [ foo = "$bar" ] # Also right![[ $foo = bar && $bar = foo ]] # Also right!

6. [[ $foo > 7 ]]

很可惜 [[ 只适用于字符串,不能做数字比较。数字比较应当这样写:
(( $foo > 7 ))

或 者用经典的写法:
[ $foo -gt 7 ]

但上述使用 -gt 的写法有个问题,那就是当 $foo 不是数字时就会出错。你必须做好类型检验。

这样写也行。
[[ $foo -gt 7 ]]

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

由于格式问题,标题中我 多加了一个空格。实际的代码应该是这样的:
grep foo bar | while read line; do ((count++)); done # 错误!

这 行代码数出bar文件中包含foo的行数,虽然很麻烦(等同于grep -c foo bar或者 grep foo bar | wc -l)。乍一看没有问题,但执行之后count变量却没有值。因为管道中的每个命令都放到一个新的子shell中执行,所以子shell中定义的 count变量无法传递出来。

8. if [grep foo myfile]

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

if 会将 if 到 then 之间的所有命令的返回值当作判断条件。因此上面的语句应当写成
if grep foo myfile > /dev/null; then

9. if [bar="$foo"]

同样,[ 是个命令,不是 if 语句的一部分,所以要注意空格。
if [ bar = "$foo" ]

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

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

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

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

12. 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也别忘记给变量加引号。

13. $foo=bar

变量赋值时无需加 $ 符号——这不是Perl或PHP。

14. foo = bar

变量赋值时等号两侧不能加空格——这不是C语言。

15. echo < 分类:Linux | 标签: |


相关日志

评论被关闭!