国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
Bash的陷阱
原作者:charlee

感謝fcicq,他的new 30 days系列為我們帶來(lái)了不少好文章。

今天想分析的是這篇Bash Pitfalls,介紹了一些bash編程中的經(jīng)典錯(cuò)誤。fcicq說(shuō)可能不適合初學(xué)者,而我認(rèn)為,正是bash編程的初學(xué)者才應(yīng)該好好閱讀一下這篇文章。

下面就逐個(gè)分析一下這篇文章中提到的錯(cuò)誤。不是完全的翻譯,有些沒(méi)用的話就略過(guò)了,有些地方則加了些注釋。

1. for i in `ls *.mp3`

常見(jiàn)的錯(cuò)誤寫(xiě)法:

for i in `ls *.mp3`; do     # Wrong!

為什么錯(cuò)誤呢?因?yàn)閒or...in語(yǔ)句是按照空白來(lái)分詞的,包含空格的文件名會(huì)被拆成多個(gè)詞。如遇到 01 - Don't Eat the Yellow Snow.mp3 時(shí),i的值會(huì)依次取 01,-,Don't,等等。

用雙引號(hào)也不行,它會(huì)將ls *.mp3的全部結(jié)果當(dāng)成一個(gè)詞來(lái)處理。

for i in "`ls *.mp3`"; do   # Wrong!

正確的寫(xiě)法是

for i in *.mp3; do

2. cp $file $target

這句話基本上正確,但同樣有空格分詞的問(wèn)題。所以應(yīng)當(dāng)用雙引號(hào):

cp "$file" "$target"

但是如果湊巧文件名以 - 開(kāi)頭,這個(gè)文件名會(huì)被 cp 當(dāng)作命令行選項(xiàng)來(lái)處理,依舊很頭疼。可以試試下面這個(gè)。

cp -- "$file" "$target"

運(yùn)氣差點(diǎn)的再碰上一個(gè)不支持 -- 選項(xiàng)的系統(tǒng),那只能用下面的方法了:使每個(gè)變量都以目錄開(kāi)頭。

for i in ./*.mp3; do  cp "$i" /target  ...

3. [ $foo = "bar" ]

當(dāng)$foo為空時(shí),上面的命令就變成了

[ = "bar" ]

類(lèi)似地,當(dāng)$foo包含空格時(shí):

[ multiple words here = "bar" ]

兩者都會(huì)出錯(cuò)。所以應(yīng)當(dāng)用雙引號(hào)將變量括起來(lái):

[ "$foo" = bar ]      # 幾乎完美了。

但是!當(dāng)$foo以 - 開(kāi)頭時(shí)依然會(huì)有問(wèn)題。在較新的bash中你可以用下面的方法來(lái)代替,[[ 關(guān)鍵字能正確處理空白、空格、帶橫線等問(wèn)題。

[[ $foo = bar ]]      # 正確

舊版本bash中可以用這個(gè)技巧(雖然不好理解):

[ x"$foo" = xbar ]    # 正確

或者干脆把變量放在右邊,因?yàn)?[ 命令的等號(hào)右邊即使是空白或是橫線開(kāi)頭,依然能正常工作。(Java編程風(fēng)格中也有類(lèi)似的做法,雖然目的不一樣。)

[ bar = "$foo" ]      # 正確

4. cd `dirname "$f"`

同樣也存在空格問(wèn)題。那么加上引號(hào)吧。

cd "`dirname "$f"`"

問(wèn)題來(lái)了,是不是寫(xiě)錯(cuò)了?由于雙引號(hào)的嵌套,你會(huì)認(rèn)為`dirname 是第一個(gè)字符串,`是第二個(gè)字符串。錯(cuò)了,那是C語(yǔ)言。在bash中,命令替換(反引號(hào)``中的內(nèi)容)里面的雙引號(hào)會(huì)被正確地匹配到一起,不用特意去轉(zhuǎn)義。

$()語(yǔ)法也相同,如下面的寫(xiě)法是正確的。

cd "$(dirname "$f")"

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

[ 中不能使用 && 符號(hào)!因?yàn)?[ 的實(shí)質(zhì)是 test 命令,&& 會(huì)把這一行分成兩個(gè)命令的。應(yīng)該用以下的寫(xiě)法。

[ bar = "$foo" -a foo = "$bar" ]       # Right![ bar = "$foo" ] && [ foo = "$bar" ]   # Also right![[ $foo = bar && $bar = foo ]]         # Also right!

6. [[ $foo > 7 ]]

很可惜 [[ 只適用于字符串,不能做數(shù)字比較。數(shù)字比較應(yīng)當(dāng)這樣寫(xiě):

(( $foo > 7 ))

或者用經(jīng)典的寫(xiě)法:

[ $foo -gt 7 ]

但上述使用 -gt 的寫(xiě)法有個(gè)問(wèn)題,那就是當(dāng) $foo 不是數(shù)字時(shí)就會(huì)出錯(cuò)。你必須做好類(lèi)型檢驗(yàn)。

這樣寫(xiě)也行。

[[ $foo -gt 7 ]]

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

由于格式問(wèn)題,標(biāo)題中我多加了一個(gè)空格。實(shí)際的代碼應(yīng)該是這樣的:

grep foo bar | while read line; do ((count++)); done         # 錯(cuò)誤!

這行代碼數(shù)出bar文件中包含foo的行數(shù),雖然很麻煩(等同于grep -c foo bar或者 grep foo bar | wc -l)。乍一看沒(méi)有問(wèn)題,但執(zhí)行之后count變量卻沒(méi)有值。因?yàn)楣艿乐械拿總€(gè)命令都放到一個(gè)新的子shell中執(zhí)行,所以子shell中定義的count變量無(wú)法傳遞出來(lái)。

8. if [grep foo myfile]

初學(xué)者常犯的錯(cuò)誤,就是將 if 語(yǔ)句后面的 [ 當(dāng)作if語(yǔ)法的一部分。實(shí)際上它是一個(gè)命令,相當(dāng)于 test 命令,而不是 if 語(yǔ)法。這一點(diǎn)C程序員特別應(yīng)當(dāng)注意。

if 會(huì)將 if 到 then 之間的所有命令的返回值當(dāng)作判斷條件。因此上面的語(yǔ)句應(yīng)當(dāng)寫(xiě)成

if grep foo myfile > /dev/null; then

9. if [bar="$foo"]

同樣,[ 是個(gè)命令,不是 if 語(yǔ)句的一部分,所以要注意空格。

if [ bar = "$foo" ]

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

同樣的問(wèn)題,[ 不是 if 語(yǔ)句的一部分,當(dāng)然也不是改變邏輯判斷的括號(hào)。它是一個(gè)命令??赡蹸程序員比較容易犯這個(gè)錯(cuò)誤?

if [ a = b ] && [ c = d ]        # 正確

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

不能在同一條管道操作中同時(shí)讀寫(xiě)一個(gè)文件。根據(jù)管道的實(shí)現(xiàn)方式,file要么被截?cái)喑?字節(jié),要么會(huì)無(wú)限增長(zhǎng)直到填滿整個(gè)硬盤(pán)。如果想改變?cè)募膬?nèi)容,只能先將輸出寫(xiě)到臨時(shí)文件中再用mv命令。

sed 's/foo/bar/g' file > tmpfile && mv tmpfile file

12. echo $foo

這句話還有什么錯(cuò)誤碼?一般來(lái)說(shuō)是正確的,但下面的例子就有問(wèn)題了。

MSG="Please enter a file name of the form *.zip"echo $MSG         # 錯(cuò)誤!

如果恰巧當(dāng)前目錄下有zip文件,就會(huì)顯示成

Please enter a file name of the form freenfss.zip lw35nfss.zip

所以即使是echo也別忘記給變量加引號(hào)。

13. $foo=bar

變量賦值時(shí)無(wú)需加 $ 符號(hào)——這不是Perl或PHP。

14. foo = bar

變量賦值時(shí)等號(hào)兩側(cè)不能加空格——這不是C語(yǔ)言。

15. echo <<EOF

here document是個(gè)好東西,它可以輸出成段的文字而不用加引號(hào)也不用考慮換行符的處理問(wèn)題。不過(guò)here document輸出時(shí)應(yīng)當(dāng)使用cat而不是echo。

# This is wrong:echo <<EOFHello worldEOF# This is right:cat <<EOFHello worldEOF

16. su -c 'some command'

原文的意思是,這條基本上正確,但使用者的目的是要將 -c 'some command' 傳給shell。而恰好 su 有個(gè) -c 參數(shù),所以su 只會(huì)將 'some command' 傳給shell。所以應(yīng)該這么寫(xiě):

su root -c 'some command'

但是在我的平臺(tái)上,man su 的結(jié)果中關(guān)于 -c 的解釋為

-c, --commmand=COMMAND            pass a single COMMAND to the shell with -c

也就是說(shuō),-c 'some command' 同樣會(huì)將 -c 'some command' 這樣一個(gè)字符串傳遞給shell,和這條就不符合了。不管怎樣,先將這一條寫(xiě)在這里吧。

17. cd /foo; bar

cd有可能會(huì)出錯(cuò),出錯(cuò)后 bar 命令就會(huì)在你預(yù)想不到的目錄里執(zhí)行了。所以一定要記得判斷cd的返回值。

cd /foo && bar

如果你要根據(jù)cd的返回值執(zhí)行多條命令,可以用 ||。

cd /foo || exit 1;barbaz

關(guān)于目錄的一點(diǎn)題外話,假設(shè)你要在shell程序中頻繁變換工作目錄,如下面的代碼:

find ... -type d | while read subdir; do  cd "$subdir" && whatever && ... && cd -done

不如這樣寫(xiě):

find ... -type d | while read subdir; do  (cd "$subdir" && whatever && ...)done

括號(hào)會(huì)強(qiáng)制啟動(dòng)一個(gè)子shell,這樣在這個(gè)子shell中改變工作目錄不會(huì)影響父shell(執(zhí)行這個(gè)腳本的shell),就可以省掉cd - 的麻煩。

你也可以靈活運(yùn)用 pushd、popd、dirs 等命令來(lái)控制工作目錄。

18. [ bar == "$foo" ]

[ 命令中不能用 ==,應(yīng)當(dāng)寫(xiě)成

[ bar = "$foo" ] && echo yes[[ bar == $foo ]] && echo yes

19. for i in {1..10}; do ./something &; done

& 后面不應(yīng)該再放 ; ,因?yàn)?& 已經(jīng)起到了語(yǔ)句分隔符的作用,無(wú)需再用;。

for i in {1..10}; do ./something & done

20. cmd1 && cmd2 || cmd3

有人喜歡用這種格式來(lái)代替 if...then...else 結(jié)構(gòu),但其實(shí)并不完全一樣。如果cmd2返回一個(gè)非真值,那么cmd3則會(huì)被執(zhí)行。所以還是老老實(shí)實(shí)地用 if cmd1; then cmd2; else cmd3 為好。

21. UTF-8的BOM(Byte-Order Marks)問(wèn)題

UTF-8編碼可以在文件開(kāi)頭用幾個(gè)字節(jié)來(lái)表示編碼的字節(jié)順序,這幾個(gè)字節(jié)稱(chēng)為BOM。但Unix格式的UTF-8編碼不需要BOM。多余的BOM會(huì)影響shell解析,特別是開(kāi)頭的 #!/bin/sh 之類(lèi)的指令將會(huì)無(wú)法識(shí)別。

MS-DOS格式的換行符(CRLF)也存在同樣的問(wèn)題。如果你將shell程序保存成DOS格式,腳本就無(wú)法執(zhí)行了。

$ ./dos-bash: ./dos: /bin/sh^M: bad interpreter: No such file or directory

22. echo "Hello World!"

交互執(zhí)行這條命令會(huì)產(chǎn)生以下的錯(cuò)誤:

-bash: !": event not found

因?yàn)?!" 會(huì)被當(dāng)作命令行歷史替換的符號(hào)來(lái)處理。不過(guò)在shell腳本中沒(méi)有這樣的問(wèn)題。

不幸的是,你無(wú)法使用轉(zhuǎn)義符來(lái)轉(zhuǎn)義!:

$ echo "hi\!"hi\!

解決方案之一,使用單引號(hào),即

$ echo 'Hello, world!'

如果你必須使用雙引號(hào),可以試試通過(guò) set +H 來(lái)取消命令行歷史替換。

set +Hecho "Hello, world!"

23. for arg in $*

$*表示所有命令行參數(shù),所以你可能想這樣寫(xiě)來(lái)逐個(gè)處理參數(shù),但參數(shù)中包含空格時(shí)就會(huì)失敗。如:

#!/bin/bash# Incorrect versionfor x in $*; do  echo "parameter: '$x'"done$ ./myscript 'arg 1' arg2 arg3parameter: 'arg'parameter: '1'parameter: 'arg2'parameter: 'arg3'

正確的方法是使用 "$@"。

#!/bin/bash# Correct versionfor x in "$@"; do  echo "parameter: '$x'"done$ ./myscript 'arg 1' arg2 arg3parameter: 'arg 1'parameter: 'arg2'parameter: 'arg3'

在 bash 的手冊(cè)中對(duì) $* 和 $@ 的說(shuō)明如下:

*    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" ...  

可見(jiàn),不加引號(hào)時(shí) $* 和 $@ 是相同的,但"$*" 會(huì)被擴(kuò)展成一個(gè)字符串,而 "$@" 會(huì)被擴(kuò)展成每一個(gè)參數(shù)。

24. function foo()

在bash中沒(méi)有問(wèn)題,但其他shell中有可能出錯(cuò)。不要把 function 和括號(hào)一起使用。最為保險(xiǎn)的做法是使用括號(hào),即

foo() {  ...}
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
編寫(xiě)自己的Shell解釋器
shell中$$BASH有什么特別含義嗎
[轉(zhuǎn)載]shell中$*與$@的區(qū)別
shell調(diào)試 去掉^M
bash與dash的差別
PHP程序員如何理解依賴注入容器(dependency injection container)
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服