結(jié)構(gòu)化命令 之 for命令

本篇內(nèi)容均摘自《Linux命令行與shell腳本編程大全》杜跷,個人認(rèn)為需要重點(diǎn)學(xué)習(xí)的章節(jié)。【免費(fèi)】Linux命令行與Shell腳本編程大全 第3版 PDF全本 21MB 百度網(wǎng)盤下載 - 今夕是何夕 - 博客園
重復(fù)執(zhí)行一系列命令在編程中很常見搀别。通常你需要重復(fù)一組命令直至達(dá)到某個特定條件退个,比如處理某個目錄下的所有文件、系統(tǒng)上的所有用戶或是某個文本文件中的所有行妄呕。bash shell提供了for命令,允許你創(chuàng)建一個遍歷一系列值的循環(huán)嗽测。每次迭代都使用其中一個值來執(zhí)行已定義好的一組命令绪励。下面是bash shell中for命令的基本格式:
for var in list
do
commands
done
在list參數(shù)中肿孵,你需要提供迭代中要用到的一系列值∈栉海可以通過幾種不同的方法指定列表中的值停做。在每次迭代中,變量var會包含列表中的當(dāng)前值大莫。第一次迭代會使用列表中的第一個值蛉腌,第二次迭代使用第二個值,以此類推只厘,直到列表中的所有值都過一遍烙丛。在do和done語句之間輸入的命令可以是一條或多條標(biāo)準(zhǔn)bash shell命令。在這些命令中羔味,$var變量包含著這次迭代對應(yīng)的當(dāng)前列表項(xiàng)中的值河咽。
[說明] 也可以將do語句和for語句放在同一行,但必須用分號將其同列表中的值分開:
for var in list; do
讀取列表中的值
for命令最基本的用法就是遍歷for命令自身所定義的一系列值赋元。

$ cat test1
#!/bin/bash
for test in Alabama Alaska Arizona Arkansas California Colorado
do
  echo The next state is $test
done
$ ./test1
The next state is Alabama
The next state is Alaska
The next state is Arizona
The next state is Arkansas
The next state is California
The next state is Colorado

每次for命令遍歷值列表忘蟹,它都會將列表中的下個值賦給test變量。 test變量可以像for命令語句中的其他腳本變量一樣使用搁凸。在最后一次迭代后媚值, $test變量的值會在shell腳本的剩余部分一直保持有效。它會一直保持最后一次迭代的值(除非你修改了它)护糖。

$ cat test1b
#!/bin/bash
for test in Alabama Alaska Arizona Arkansas California Colorado
do
  echo "The next state is $test"
done
echo "The last state we visited was $test"
test=Connecticut
echo "Wait, now we're visiting $test"
$ ./test1b
The next state is Alabama
The next state is Alaska
The next state is Arizona
The next state is Arkansas
The next state is California
The next state is Colorado
The last state we visited was Colorado
Wait, now we're visiting Connecticut

$test變量保持了其值褥芒,也允許我們修改它的值,并在for命令循環(huán)之外跟其他變量一樣使用嫡良。
讀取列表中的復(fù)雜值
有時會遇到難處理的數(shù)據(jù)喂很。下面是給shell腳本程序員帶來麻煩的典型例子:

$ cat badtest1
#!/bin/bash
for test in I don't know if this'll work
do
  echo "word:$test"
done
$ ./badtest1
word:I
word:dont know if thisll
word:work

shell看到了列表值中的單引號并嘗試使用它們來定義一個單獨(dú)的數(shù)據(jù)值,有兩種辦法可解決這個問題:
1.使用轉(zhuǎn)義字符(反斜線)來將單引號轉(zhuǎn)義;
2.使用雙引號來定義用到單引號的值皆刺。

$ cat test2
#!/bin/bash
for test in I don\'t know if "this'll" work
do
  echo "word:$test"
done
$ ./test2
word:I
word:don't
word:know
word:if
word:this'll
word:work

記住for循環(huán)假定每個值都是用空格分割的少辣。如果有包含空格的數(shù)據(jù)值,你就陷入麻煩了羡蛾。

$ cat badtest2
#!/bin/bash
for test in Nevada New Hampshire New Mexico New York North Carolina
do
  echo "Now going to $test"
done
$ ./badtest1
Now going to Nevada
Now going to New
Now going to Hampshire
Now going to New
Now going to Mexico
Now going to New
Now going to York
Now going to North
Now going to Carolina

這不是我們想要的結(jié)果漓帅。 for命令用空格來劃分列表中的每個值。如果在單獨(dú)的數(shù)據(jù)值中有空格痴怨,就必須用雙引號將這些值圈起來忙干。

$ cat test3
#!/bin/bash
for test in Nevada "New Hampshire" "New Mexico" "New York"
do
  echo "Now going to $test"
done
$ ./test3
Now going to Nevada
Now going to New Hampshire
Now going to New Mexico
Now going to New York

現(xiàn)在for命令可以正確區(qū)分不同值了。另外要注意的是浪藻,在某個值兩邊使用雙引號時捐迫, shell并不會將雙引號當(dāng)成值的一部分。從變量讀取列表通常shell腳本遇到的情況是爱葵,你將一系列值都集中存儲在了一個變量中施戴,然后需要遍歷變量中的整個列表反浓。也可以通過for命令完成這個任務(wù)。

$ cat test4
#!/bin/bash
list="Alabama Alaska Arizona Arkansas Colorado"
list=$list" Connecticut"
for state in $list
do
  echo "Have you ever visited $state?"
done
$ ./test4
Have you ever visited Alabama?
Have you ever visited Alaska?
Have you ever visited Arizona?
Have you ever visited Arkansas?
Have you ever visited Colorado?
Have you ever visited Connecticut?

list變量包含了用于迭代的標(biāo)準(zhǔn)文本值列表赞哗。注意雷则,代碼還是用了另一個賦值語句向$list變量包含的已有列表中添加(或者說是拼接)了一個值。這是向變量中存儲的已有文本字符串尾部添加文本的一個常用方法肪笋。
從命令讀取值
生成列表中所需值的另外一個途徑就是使用命令的輸出月劈。可以用命令替換來執(zhí)行任何能產(chǎn)生輸出的命令藤乙,然后在for命令中使用該命令的輸出猜揪。

$ cat test5
#!/bin/bash
file="states"
for state in $(cat $file)
do
  echo "Visit beautiful $state"
done
$ cat states
Alabama
Alaska
Arizona
Arkansas
Colorado
Connecticut
Delaware
Florida
Georgia
$ ./test5
Visit beautiful Alabama
Visit beautiful Alaska
Visit beautiful Arizona
Visit beautiful Arkansas
Visit beautiful Colorado
Visit beautiful Connecticut
Visit beautiful Delaware
Visit beautiful Florida
Visit beautiful Georgia

這個例子在命令替換中使用了cat命令來輸出文件states的內(nèi)容。你會注意到states文件中每一行有一個州坛梁,而不是通過空格分隔的而姐。 for命令仍然以每次一行的方式遍歷了cat命令的輸出,假定每個州都是在單獨(dú)的一行上罚勾。但這并沒有解決數(shù)據(jù)中有空格的問題。如果你列出了一個名字中有空格的州吭狡, for命令仍然會將每個單詞當(dāng)作單獨(dú)的值尖殃。說明: test5的代碼范例將文件名賦給變量,文件名中沒有加入路徑划煮。這要求文件和腳本位于同一個目錄中送丰。如果不是的話,你需要使用全路徑名(不管是絕對路徑還是相對路徑)來引用文件位置弛秋。
更改字段分隔符
造成這個問題的原因是特殊的環(huán)境變量IFS器躏,叫作內(nèi)部字段分隔符( internal field separator)。IFS環(huán)境變量定義了bash shell用作字段分隔符的一系列字符蟹略。默認(rèn)情況下登失, bash shell會將下列字符當(dāng)作字段分隔符:空格,制表符(\t)挖炬,換行符(\n)揽浙。如果bash shell在數(shù)據(jù)中看到了這些字符中的任意一個,它就會假定這表明了列表中一個新數(shù)據(jù)字段的開始意敛。在處理可能含有空格的數(shù)據(jù)(比如文件名)時馅巷,這會非常麻煩,就像你在上一個腳本示例中看到的草姻。要解決這個問題钓猬,可以在shell腳本中臨時更改IFS環(huán)境變量的值來限制被bash shell當(dāng)作字段分隔符的字符。例如撩独,如果你想修改IFS的值敞曹,使其只能識別換行符账月,那就必須這么做:
IFS=$'\n'
將這個語句加入到腳本中,告訴bash shell在數(shù)據(jù)值中忽略空格和制表符异雁。對前一個腳本使用這種方法捶障,將獲得如下輸出。

$ cat test5b
#!/bin/bash
file="states"
IFS=$'\n'
for state in $(cat $file)
do
  echo "Visit beautiful $state"
done
$ ./test5b
Visit beautiful Alabama
Visit beautiful Alaska
Visit beautiful Arizona
Visit beautiful Arkansas
Visit beautiful Colorado
Visit beautiful Connecticut
Visit beautiful Delaware
Visit beautiful Florida
Visit beautiful Georgia
Visit beautiful New York
Visit beautiful New Hampshire
Visit beautiful North Carolina

現(xiàn)在纲刀, shell腳本就能夠使用列表中含有空格的值了项炼。
警告:在處理代碼量較大的腳本時,可能在一個地方需要修改IFS的值示绊,然后忽略這次修改锭部,在腳本的其他地方繼續(xù)沿用IFS的默認(rèn)值。一個可參考的安全實(shí)踐是在改變IFS之前保存原來的IFS值面褐,之后再恢復(fù)它拌禾。這種技術(shù)可以這樣實(shí)現(xiàn):

IFS.OLD=$IFS
IFS=$'\n'  #在代碼中使用新的IFS值
IFS=$IFS.OLD

這就保證了在腳本的后續(xù)操作中使用的是IFS的默認(rèn)值。
還有其他一些IFS環(huán)境變量的絕妙用法展哭。假定你要遍歷一個文件中用冒號分隔的值(比如在/etc/passwd文件中)湃窍。你要做的就是將IFS的值設(shè)為冒號。
IFS=:
如果要指定多個IFS字符匪傍,只要將它們在賦值行串起來就行您市。
IFS=$'\n':;"
這個賦值會將換行符、冒號役衡、分號和雙引號作為字段分隔符茵休。如何使用IFS字符解析數(shù)據(jù)沒有任何限制。

用通配符讀取目錄
最后手蝎,可以用for命令來自動遍歷目錄中的文件榕莺。進(jìn)行此操作時,必須在文件名或路徑名中使用通配符棵介。它會強(qiáng)制shell使用文件擴(kuò)展匹配钉鸯。文件擴(kuò)展匹配是生成匹配指定通配符的文件名或路徑名的過程。如果不知道所有的文件名邮辽,這個特性在處理目錄中的文件時就非常好用亏拉。

$ cat test6
#!/bin/bash
for file in /home/rich/test/*
do
  if [ -d "$file" ]
  then
    echo "$file is a directory"
  elif [ -f "$file" ]
  then
    echo "$file is a file"
fi
done
$ ./test6
/home/rich/test/dir1 is a directory
/home/rich/test/myprog.c is a file
/home/rich/test/myprog is a file
/home/rich/test/myscript is a file
/home/rich/test/newdir is a directory
/home/rich/test/newfile is a file
/home/rich/test/newfile2 is a file
/home/rich/test/testdir is a directory
/home/rich/test/testing is a file
/home/rich/test/testprog is a file
/home/rich/test/testprog.c is a file

for命令會遍歷/home/rich/test/*輸出的結(jié)果。該代碼用test命令測試了每個條目(使用方括號方法)逆巍,以查看它是目錄(通過-d參數(shù))還是文件(通過-f參數(shù))及塘。
注意:我們在這個例子的if語句中做了一些不同的處理:

if [ -d "$file" ]

在Linux中,目錄名和文件名中包含空格當(dāng)然是合法的锐极。要適應(yīng)這種情況笙僚,應(yīng)該將$file變量用雙引號圈起來。如果不這么做灵再,遇到含有空格的目錄名或文件名時就會有錯誤產(chǎn)生肋层。
./test6: line 6: [: too many arguments
./test6: line 9: [: too many arguments
在test命令中亿笤, bash shell會將額外的單詞當(dāng)作參數(shù),進(jìn)而造成錯誤栋猖。
也可以在for命令中列出多個目錄通配符净薛,將目錄查找和列表合并進(jìn)同一個for語句。

$ cat test7
#!/bin/bash
for file in /home/rich/.b* /home/rich/badtest
do
  if [ -d "$file" ]
  then
    echo "$file is a directory"
  elif [ -f "$file" ]
  then
    echo "$file is a file"
  else
    echo "$file doesn't exist"
  fi
done
$ ./test7
/home/rich/.backup.timestamp is a file
/home/rich/.bash_history is a file
/home/rich/.bash_logout is a file
/home/rich/.bash_profile is a file
/home/rich/.bashrc is a file
/home/rich/badtest doesn't exist

for語句首先使用了文件擴(kuò)展匹配來遍歷通配符生成的文件列表蒲拉,然后它會遍歷列表中的下一個文件肃拜。可以將任意多的通配符放進(jìn)列表中雌团。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末燃领,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子锦援,更是在濱河造成了極大的恐慌猛蔽,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件灵寺,死亡現(xiàn)場離奇詭異曼库,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)略板,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門毁枯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蚯根,你說我怎么就攤上這事后众≌兔樱” “怎么了颅拦?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長教藻。 經(jīng)常有香客問我距帅,道長,這世上最難降的妖魔是什么括堤? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任碌秸,我火速辦了婚禮,結(jié)果婚禮上悄窃,老公的妹妹穿的比我還像新娘讥电。我一直安慰自己,他們只是感情好轧抗,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布恩敌。 她就那樣靜靜地躺著,像睡著了一般横媚。 火紅的嫁衣襯著肌膚如雪纠炮。 梳的紋絲不亂的頭發(fā)上月趟,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機(jī)與錄音恢口,去河邊找鬼孝宗。 笑死,一個胖子當(dāng)著我的面吹牛耕肩,可吹牛的內(nèi)容都是我干的因妇。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼看疗,長吁一口氣:“原來是場噩夢啊……” “哼沙峻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起两芳,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤摔寨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后怖辆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體是复,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年竖螃,在試婚紗的時候發(fā)現(xiàn)自己被綠了淑廊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡特咆,死狀恐怖季惩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情腻格,我是刑警寧澤画拾,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站菜职,受9級特大地震影響青抛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜酬核,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一蜜另、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嫡意,春花似錦举瑰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春邮屁,著一層夾襖步出監(jiān)牢的瞬間整袁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工佑吝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坐昙,地道東北人。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓芋忿,卻偏偏與公主長得像炸客,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子戈钢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 官網(wǎng) 中文版本 好的網(wǎng)站 Content-type: text/htmlBASH Section: User ...
    不排版閱讀 4,367評論 0 5
  • 一痹仙、Python簡介和環(huán)境搭建以及pip的安裝 4課時實(shí)驗(yàn)課主要內(nèi)容 【Python簡介】: Python 是一個...
    _小老虎_閱讀 5,723評論 0 10
  • 第 2 章 SHELL 基礎(chǔ)知識2.1 shell腳本我們在上面簡單介紹了一下什么是shell腳本,現(xiàn)在我們來進(jìn)一...
    LiWei_9e4b閱讀 1,554評論 0 0
  • 內(nèi)容for循環(huán)語句until迭代語句使用while語句循環(huán)重定向循環(huán)的輸出 這一節(jié)我們來了解如何重復(fù)一些過程和命令...
    王詩翔閱讀 741評論 0 2
  • 個人學(xué)習(xí)批處理的初衷來源于實(shí)際工作殉了;在某個迭代版本有個BS(安卓手游模擬器)大需求开仰,從而在測試過程中就重復(fù)涉及到...
    Luckykailiu閱讀 4,691評論 0 11