色偷偷91综合久久噜噜-色偷偷成人-色偷偷尼玛图亚洲综合-色偷偷人人澡久久天天-国内精品视频一区-国内精品视频一区二区三区

Hello! 歡迎來(lái)到小浪云!


第一節(jié):Bash編程易犯的錯(cuò)誤


第一節(jié):Bash編程易犯的錯(cuò)誤

前一段時(shí)間發(fā)現(xiàn)一個(gè)很好的wiki站點(diǎn),上面有很多優(yōu)秀的bash文章。最近挑了一篇介紹Bash編程容易犯的各種錯(cuò)誤的文章看,收獲很多,不感獨(dú)享,把這篇文章以半翻譯半筆記的形式分享給大家。

1. for i in $(ls *.mp3)

Bash寫循環(huán)代碼的時(shí)候,確實(shí)比較容易犯下面的錯(cuò)誤:

 for i in $(ls *.mp3); do    # 錯(cuò)誤!     some command $i         # 錯(cuò)誤! done  for i in $(ls)              # 錯(cuò)誤! for i in `ls`               # 錯(cuò)誤!  for i in $(find . -type f)  # 錯(cuò)誤! for i in `find . -type f`   # 錯(cuò)誤!  files=($(find . -type f))   # 錯(cuò)誤! for i in ${files[@]}        # 錯(cuò)誤! 

這里主要兩個(gè)問題:

使用命令展開時(shí)不帶引號(hào),其執(zhí)行結(jié)果會(huì)使用ifS作為分隔符,拆分成參數(shù)傳遞給for循環(huán)處理;

不應(yīng)該讓腳本去解析ls命令的結(jié)果;

我們不能避免某些文件名中包含空格,Shell會(huì)對(duì)$(ls *.mp3)展開的結(jié)果會(huì)被做單詞拆分(WordSplitting)的處理。假設(shè)有一個(gè)文件,名字為01 – Don’t Eat the Yellow Snow.mp3,for循環(huán)處理的時(shí)候,會(huì)今次遍歷文件名中的每個(gè)單詞:01, -, Don’t, Eat等等:

 $ for i in $(ls *.mp3); do echo $i; done 01 - Don't Eat the Yellow Snow.mp3 

比這更差的情況是,上面命令展開的結(jié)果可能被Shell進(jìn)一步處理,比如文件名展開。比如,ls執(zhí)行的結(jié)果中包含*號(hào),按照通配符的規(guī)則, *號(hào)會(huì)被展開成當(dāng)前目錄下的所有文件:

 $ touch "1*.mp3" "1.mp3" "11.mp3" "12.mp3" $ for i in $(ls *.mp3); do echo $i; done 1*.mp3 1.mp3 11.mp3 12.mp3 1.mp3 11.mp3 12.mp3 1.mp3 11.mp3 12.mp3 

不過(guò),在這種場(chǎng)景下,你即使加上引號(hào),也是無(wú)濟(jì)于事的:

 $ for i in "$(ls *.mp3)"; do echo --$i--; done --1*.mp3 1.mp3 11.mp3 12.mp3-- 

加上引號(hào)后,ls執(zhí)行的結(jié)果會(huì)被當(dāng)成一個(gè)整體,所以for循環(huán)只會(huì)執(zhí)行一次,達(dá)不到預(yù)期的效果。

事實(shí)上,這種情況下,根本不需要使用ls命令。ls命令的結(jié)果本身就設(shè)計(jì)成給人讀的,而不是給腳本解析的。正確的處理方法是,直接使用文件名展開(通配符)的功能:

 $ for i in *.mp3; do >     echo "$i" > done 1*.mp3 1.mp3 11.mp3 12.mp3 

文件名展開是位于各種展開(花括號(hào)展開、變量替換、命令展開等)功能中的最后一個(gè)環(huán)節(jié),所以不會(huì)有之前不帶引號(hào)的命令展開的副作用。如果你需要遞歸地處理文件,可以考慮使用Find命令。

到這一步,之間的問題看樣子已經(jīng)修復(fù)了。但是,如果你進(jìn)一步思考,假設(shè)當(dāng)前目錄上沒有文件時(shí)會(huì)怎么樣?沒有文件的時(shí)候,*.mp3不會(huì)被展開直接傳遞給for循環(huán)處理,所以這個(gè)時(shí)候循環(huán)還是會(huì)執(zhí)行一次。這種情況不是我們預(yù)期的行為。

保險(xiǎn)起見,可以在循環(huán)處理的時(shí)候,檢查下文件是否存在:

 # POSIX for i in *.mp3; do     [ -e "$i" ] || continue     some command "$i" done 

如果你有使用引號(hào)和避免單詞拆分的習(xí)慣,你完全可以避免很多錯(cuò)誤。

注意下循環(huán)體內(nèi)部的”$i”,這里會(huì)導(dǎo)致下面我們要說(shuō)的另外一個(gè)比較容易犯的錯(cuò)誤。

2. cp $file $target

上面的命令有什么問題呢?如果你提前知道,$file和$target文件名中不會(huì)包含空格或者*號(hào)。否則,這行命令執(zhí)行前在經(jīng)過(guò)單詞拆分和文件名展開的時(shí)候會(huì)出現(xiàn)問題。所以,兩次強(qiáng)調(diào),在使用展開的地方切勿忘記使用引號(hào):

 $ cp -- "$file" "$target"  如果不帶引號(hào),當(dāng)你執(zhí)行如下命令時(shí)就會(huì)出錯(cuò):  $ file="01 - Don't Eat the Yellow Snow.mp3" $ target="/tmp" $ cp $file $target cp: cannot stat ‘01’: No such file or directory .. 

如果帶上引號(hào),就不會(huì)有上面的問題,除非文件名以’-‘開頭,在這種情況下,cp會(huì)認(rèn)為你提供的是一個(gè)命令行選項(xiàng),這個(gè)錯(cuò)誤下面會(huì)介紹。

3. 文件名中包含短橫’-‘

文件名以’-‘開頭會(huì)導(dǎo)致許多問題,*.mp3這種通配符會(huì)根據(jù)當(dāng)前的locale展開成一個(gè)列表,但在絕大多數(shù)環(huán)境下,’-‘排序的時(shí)候會(huì)排在大多數(shù)字母前。這個(gè)展開的列表傳遞給有些命令的時(shí)候,會(huì)錯(cuò)誤的將-filename解析成命令行選項(xiàng)。這里有兩種方法來(lái)解決這個(gè)問題。

第一種方法是在命令和參數(shù)之間加上–,這種語(yǔ)法告訴命令不要繼續(xù)對(duì)–之后的內(nèi)容進(jìn)行命令行參數(shù)/選項(xiàng)解析:

 $ cp -- "$file" "$target" 

這種方法可以解這個(gè)問題,但是你需要在每個(gè)命令后面都要加上–,而且依賴具體的命令解析的方式,如果一些命令不兼容這種約定俗成的規(guī)范,這種做法是無(wú)效的。

另外一種方法是,確保文件名都使用相對(duì)或者絕對(duì)的路徑,以目錄開頭:

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

這種情況下,即使某個(gè)文件以-開頭,展開后文件名依然是./-foo.mp3這種形式,完全不會(huì)有問題。

4. [ $foo = “bar” ]

這是一個(gè)與第2個(gè)問題類似的問題,雖然用到了引號(hào),但是放錯(cuò)了位置,對(duì)于字符串字面值,除非有特殊符號(hào),否則不大需要用引號(hào)括起來(lái)。但是,你應(yīng)該把變量的值用括號(hào)括起來(lái),從而避免它們包含空格或能通配符,這一點(diǎn)我們?cè)谇懊娴膯栴}中都解釋過(guò)。

這個(gè)例子在以下情況下會(huì)出錯(cuò):

如果[中的變量不存在,或者為空,這個(gè)時(shí)候上面的例子最終解析結(jié)果是:

[ = “bar” ] # 錯(cuò)誤!

并且執(zhí)行會(huì)出錯(cuò):unary operator expected,因?yàn)?是二元操作符,它需要左右各一個(gè)操作數(shù)。

如果變量值包含空格,它首先在執(zhí)行之前進(jìn)行單詞拆分,因此[命令看到的樣子可能是這樣的:

 [ multiple words here = "bar" ];  正確的做法應(yīng)該是:  # POSIX [ "$foo" = bar ] 

這種寫法,在POSIX兼容的實(shí)現(xiàn)中都不會(huì)有問題,即使$foo以短橫”-“開頭,因?yàn)镻OSIX實(shí)現(xiàn)的test命令通過(guò)傳遞的參數(shù)來(lái)確定執(zhí)行的行為。

只有一些非常古老的shell可能會(huì)遇到問題,這個(gè)時(shí)候你可以使用下面的寫法來(lái)解決(相信你肯定看到過(guò)這種寫法):

 # POSIX / Bourne [ x"$foo" = xbar ]  在Bash中,還有另外一種選擇是使用[[關(guān)鍵字:  # Bash / Ksh [[ $foo == bar ]] 

這里你不需要使用引號(hào),因?yàn)樵赱[里面參數(shù)不會(huì)進(jìn)行展開,當(dāng)然帶上引號(hào)也不會(huì)有錯(cuò)。

不過(guò)有一點(diǎn)要注意的是,[[里的==不僅僅是文本比較,它會(huì)檢查左邊的值是否匹配右側(cè)的表達(dá)式,==右側(cè)的值加上引號(hào),會(huì)讓它成為一個(gè)普通的字面量,*?等通配符會(huì)失去特殊含義。

5. cd $(dirname “$f”)

這又是一個(gè)引號(hào)的問題,命令展開的結(jié)果會(huì)進(jìn)一步地進(jìn)行單詞拆分或者文件名展開。因此下面的寫法才是正確的:

 cd "$(dirname "$f")" 

但是,上面引號(hào)的寫法可能比較怪異,你可能會(huì)認(rèn)為第一、二個(gè)引號(hào),第三、四個(gè)引號(hào)是一組的。

但是事實(shí)上,Bash將命令替換里面的引號(hào)當(dāng)成一組,外面的當(dāng)成另外一組。如果你是用反引號(hào)的寫法,引號(hào)的行為就不是這樣的了,所以$()寫法更加推薦。

6. [ “$foo” = bar && “$bar” = foo ]

不要在test命令內(nèi)部使用&&,Bash解析器會(huì)把你的命令分隔成兩個(gè)命令,在&&之前和之后。你應(yīng)該使用下面的寫法:

 [ bar = "$foo" ] && [ foo = "$bar" ] # POSIX [[ $foo = bar && $bar = foo ]]       # Bash / Ksh 

盡量避免使用下面的寫法,雖然它是正確的,但是這種寫法可移植性不好,并且已經(jīng)在POSIX-2008中被廢棄:

 [ bar = "$foo" -a foo = "$bar" ] 

7. [[ $foo > 7 ]]

原文作者認(rèn)為算術(shù)比較不應(yīng)該用[[,而是用((,我沒弄明白是為什么。

如果有理解的同學(xué),歡迎以評(píng)論回復(fù),謝謝。

8. grep foo bar | while read -r; do ((count++)); done

這種寫法初看沒有問題,但是你會(huì)發(fā)現(xiàn)當(dāng)執(zhí)行完后,count變量并沒有變化。原因是管道后面的命令是在一個(gè)子Shell中執(zhí)行的。

POSIX規(guī)范并沒有說(shuō)明管道的最后一個(gè)命令是不是在子Shell中執(zhí)行的。一些shell,例如ksh93或者Bash>=4.2可以通過(guò)shopt -s lastpipe命令,指明管道中的最后一個(gè)命令在當(dāng)前shell中執(zhí)行。由于篇幅限制,在此就不展開,有興趣的可以看Bash FAQ #24。

9. if [grep foo myfile]

初學(xué)者會(huì)錯(cuò)誤地認(rèn)為,[是if語(yǔ)法的一部分,正如c語(yǔ)言中的if ()。但是事實(shí)并非如此,if后面跟著的是一個(gè)命令,[是一個(gè)命令,它是內(nèi)置命令test的簡(jiǎn)寫形式,只不過(guò)它要求最后一個(gè)參數(shù)必須是]。下面兩種寫法是一樣的:

 # POSIX if [ false ]; then echo "help"; fi if test false; then echo "HELP"; fi 

兩個(gè)都是檢查參數(shù)”false”是不是非空的,所以上面兩個(gè)語(yǔ)句都會(huì)輸出HELP。

if語(yǔ)句的語(yǔ)法是:

 if COMMANDS then <commands> elif </commands><commands> # optional then </commands><commands> else </commands><commands> # optional fi # required </commands>

再次強(qiáng)調(diào),[是一個(gè)命令,它同其它常規(guī)的命令一樣接受參數(shù)。if是一個(gè)復(fù)合命令,它包含其它命令,[并不是if語(yǔ)法中的一部分。

如果你想根據(jù)grep命令的結(jié)果來(lái)做事情,你不需要把grep放到[里面,只需要在if后面緊跟grep即可:

 if grep -q fooregex myfile; then ... fi 

如果grep在myfile中找到匹配的行,它的執(zhí)行結(jié)果為0(true),then后面的部分就會(huì)執(zhí)行。

10. if [bar=”$foo”]; then …

正如上一個(gè)問題中提到的,[是一個(gè)命令,它的參數(shù)之間必須用空格分隔。

11. if [ [ a = b ] && [ c = d ] ]; then …

不要用把[命令看成C語(yǔ)言中if語(yǔ)句的條件一樣,它是一個(gè)命令。

如果你想表達(dá)一個(gè)復(fù)合的條件表達(dá)式,可以這樣寫:

 if [ a = b ] && [ c = d ]; then ... 

注意,if后面有兩個(gè)命令,它們用&&分開。等價(jià)于下面的寫法:

 if test a = b && test c = d; then ... 

如果第一個(gè)test(或者[)命令返回false,then后面的語(yǔ)句不會(huì)執(zhí)行;如果第一個(gè)返回true,第二個(gè)test命令會(huì)執(zhí)行;只有第二個(gè)命令同樣返回true的情況下,then后面的語(yǔ)句才會(huì)執(zhí)行。

除此之外,還可以使用[[關(guān)鍵字,因?yàn)樗С?amp;&的用法:

 if [[ a = b && c = d ]]; then ... 

12. read $foo

read命令中你不需要在變量名之前使用$。如果你想把讀入的數(shù)據(jù)存放到名為foo的變量中,下面的寫法就夠了:

 read foo 

或者,更加安全地方法:

 IFS= read -r foo read $foo會(huì)把一行的內(nèi)容讀入到變量中,該變量的名稱存儲(chǔ)在$foo中。所以兩者的含義是完全不一樣的。 

相關(guān)閱讀

主站蜘蛛池模板: 久久综合色之久久综合 | 日韩 视频在线播放 | 99精品久久 | 福利一区福利二区 | 日韩免费在线看 | 夜夜狠操 | a级毛片在线播放 | 久久精品免费全国观看国产 | 日本五十路六十30人8时间 | 日韩欧美精品在线 | 日本剧免费网站 | 最新步兵社区在线观看 | 很很射影院 | 伊人影院综合 | 丝袜中文字幕 | 在线jyzzjyzz免费视频 | 国产成人综合亚洲一区 | 韩国日本三级在线 | 黄页网址免费观看18网站 | 精品国产乱码一区二区三区 | 国产一区二区三区免费视频 | 欧美专区一区二区三区 | 亚洲欧美综合国产精品一区 | 日韩区欧美区 | 美女一级毛片免费不卡视频 | 国产在线视频二区 | 视频成人永久免费视频 | 特级毛片免费播放 | 色婷婷一区二区三区四区成人网 | 韩国一级爽快片淫片高清 | 亚洲欧美日韩专区一 | 天天插天天 | 国产八区 | 国产黄色在线视频 | 老司机精品在线播放 | 成年人在线免费看视频 | 老年女人乱色视频 | 黄页视频免费在线观看 | 久久夜色精品国产噜噜 | 一级视频在线观看免费 | 久久久国产成人精品 |