上一篇文章參見 第三節:bash編程易犯的錯誤。這一篇翻譯得不是非常滿意,時間比較趕,請見諒,如果有問題可以在本文后方留言,大家一起深入探討。
36. [ -n $foo ] or [ -z $foo ]
這個例子中,$foo 沒有用引號引起來,當$foo包含空格或者$foo為空時都會出問題:
$ foo="some word" && [ -n $foo ] && echo yes -bash: [: some: binary operator expected $ foo="" && [ -n $foo ] && echo yes yes 正確的寫法是: [ -n "$foo" ] [ -z "$foo" ] [ -n "$(some command with a "$file" in it)" ] [[ -n $foo ]] [[ -z $foo ]]
37. [[ -e “$broken_symlink” ]] returns 1 even though $broken_symlink exists
這里-e 選項是看文件是否存在,當緊跟的文件是一個軟鏈接時,它不看軟鏈接是否存在,而是看實際指向的文件是否存在。所以當軟鏈接損壞時,即實際指向的文件被刪除后,-e 的結果返回1。
所以如果你確實要判斷后面的文件是否存在,正確的寫法是:
[[ -e "$broken_symlink" || -L "$broken_symlink" ]]
38. ed file ails ed 命令使用的正則語法,不支持0次出現次數,下面的就可以正常工作:
ed file < <<"g/d{1,3}/s//e/g"
略過,現在很少會有人用 ed 命令吧。
39. expr sub-string fails for “match”
下面的例子多數情況下運行不會有問題:
word=abcde expr "$word" : ".(.*)" bcde
但是當 $work 不巧剛好是 match 時,就有可能出錯了(MAC OSX 下的 expr 命令不支持 match,所以依然能正常工作):
word=match expr "$word" : ".(.*)"
原因是 match 是 expr 命令里面的一個特殊關鍵字,針對 GNU系統,解決方法是在前面加一個’+’:
word=match expr + "$word" : ".(.*)" atch
‘+’號可以讓 expr 命令忽略后續 token 的特殊含義。
另外一個建議是,不要再使用 expr 命令了,expr 能做的事情都可以用 Bash 原生支持的參數展開(Parameter Expansion)或者字符串展開(Substring Expansion)來完成。并且相同情況下,內置的功能肯定比外部命令的效率要高。
上面的例子,目的是為了刪除單詞中的首字符,可以這樣做:
$ word=match $ echo "${word#?}" # PE atch $ echo "${word:1}" # SE atch
40. On UTF-8 and Byte-Order Marks (BOM)
多數情況下,UNIX 下 UTF-8 類型的文本不需要使用 BOM,文本的編碼是根據當前語言環境,MIME類型或者其它文件元數據信息確定的。人為閱讀時,不會因為在文件開始處加 BOM 標記而腚影響,但是當文件要被腳本解釋執行時,BOM 標記會像 MS-DOS 下的換行符(^M)一樣奇怪。
41. content=$(
這是一個很常見的錯誤,顯然你本來是想將標準輸出與標準錯誤輸出都重定向到文件logfile 中,但是你會驚訝地發現,標準錯誤依然輸出到屏幕中。
這種行為的原因是,重定向在命令執行之前解析,并且是從左往右解析。上面的命令可以翻譯成,將標準錯誤輸出重定向到標準輸出(此刻是終端),然后將標準輸出重定向到文件 logfile 中。所以,到最后,標準錯誤并沒有重定向到文件中,而是依然輸出到終端:
somecmd >>logfile 2>&1
更加詳細的說明見BashFAQ。
43. cmd; (( ! $? )) || die
只有需要捕獲上一個命令的執行結果進,才需要記錄$?的值,否則如果你只需要檢查上一個命令是否執行成功,直接檢測命令:
if cmd; then ... fi 或者使用 case 語句來檢測多個或能的返回碼: cmd status=$? case $status in 0) echo success >&2 ;; 1) echo 'Must supply a parameter, exiting.' >&2 exit 1 ;; *) echo 'Unknown error, exiting.' >&2 exit $status esac