如何在Bash中使用循環(huán)
使用循環(huán)和查找命令批量自動(dòng)對(duì)多個(gè)文件進(jìn)行一系列的操作。
人們希望學(xué)習(xí)批處理命令的一個(gè)普遍原因是要得到批處理強(qiáng)大的功能。如果你希望批量的對(duì)文件執(zhí)行一些指令,構(gòu)造一個(gè)可以重復(fù)運(yùn)行在那些文件上的命令就是一種方法。在編程術(shù)語(yǔ)中,這被稱作執(zhí)行控制,for 循環(huán)就是其中最常見(jiàn)的一種。
for 循環(huán)可以詳細(xì)描述你希望計(jì)算機(jī)對(duì)你指定的每個(gè)數(shù)據(jù)對(duì)象(比如說(shuō)文件)所進(jìn)行的操作。
一般的循環(huán)
使用循環(huán)的一個(gè)簡(jiǎn)單例子是對(duì)一組文件進(jìn)行分析。這個(gè)循環(huán)可能沒(méi)什么用,但是這是一個(gè)安全的證明自己有能力獨(dú)立處理文件夾里每一個(gè)文件的方法。首先,創(chuàng)建一個(gè)文件夾然后拷貝一些文件(例如 JPEG、PNG 等類似的文件)至文件夾中生成一個(gè)測(cè)試環(huán)境。你可以通過(guò)文件管理器或者終端來(lái)完成創(chuàng)建文件夾和拷貝文件的操作:
$ mkdir example$ cp ~/Pictures/vacation/*.{png,jpg} example
切換到你剛創(chuàng)建的那個(gè)新文件夾,然后列出文件并確認(rèn)這個(gè)測(cè)試環(huán)境是你需要的:
$ cd example$ ls -1cat.jpgdesign_maori.pngotago.jpgwaterfall.png
在循環(huán)中逐一遍歷文件的語(yǔ)法是:首先聲明一個(gè)變量(例如使用 f 代表文件),然后定義一個(gè)你希望用變量循環(huán)的數(shù)據(jù)集。在這種情況下,使用 * 通配符來(lái)遍歷當(dāng)前文件夾下的所有文件(通配符 * 匹配所有文件)。然后使用一個(gè)分號(hào)(;)來(lái)結(jié)束這個(gè)語(yǔ)句。
$ for f in * ;
取決于你個(gè)人的喜好,你可以選擇在這里按下回車鍵。在語(yǔ)法完成前,shell 是不會(huì)嘗試執(zhí)行這個(gè)循環(huán)的。
接下來(lái),定義你想在每次循環(huán)中進(jìn)行的操作。簡(jiǎn)單起見(jiàn),使用 file 命令來(lái)得到 f 變量(使用 $ 告訴 shell 使用這個(gè)變量的值,無(wú)論這個(gè)變量現(xiàn)在存儲(chǔ)著什么)所存儲(chǔ)著的文件的各種信息:
do file $f ;
使用另一個(gè)分號(hào)結(jié)束這一行,然后關(guān)閉這個(gè)循環(huán):
done
按下回車鍵啟動(dòng) shell 對(duì)當(dāng)前文件夾下所有東西的遍歷。for 循環(huán)將會(huì)一個(gè)一個(gè)的將文件分配給變量 f 并且執(zhí)行你的命令:
$ for f in * ; do> file $f ;> donecat.jpg: JPEG image data, EXIF standard 2.2design_maori.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlacedotago.jpg: JPEG image data, EXIF standard 2.2waterfall.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced
你也可以用這種形式書寫命令:
$ for f in *; do file $f; donecat.jpg: JPEG image data, EXIF standard 2.2design_maori.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlacedotago.jpg: JPEG image data, EXIF standard 2.2waterfall.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced
對(duì)你的 shell 來(lái)說(shuō),多行和單行的格式?jīng)]有什么區(qū)別,并且會(huì)輸出完全一樣的結(jié)果。
一個(gè)實(shí)用的例子
下面是一個(gè)循環(huán)在日常使用中的實(shí)用案例。假如你擁有一堆假期拍的照片想要發(fā)給你的朋友。但你的照片太大了,無(wú)法通過(guò)電子郵件發(fā)送,上傳到圖片分享服務(wù)也不方便。因此你想為你的照片創(chuàng)建小型的 web 版本,但是你不希望花費(fèi)太多時(shí)間在一個(gè)一個(gè)的壓縮圖片體積上。
首先,在你的 Linux、BSD 或者 Mac 上使用包管理器安裝 ImageMagick 命令。例如,在 Fedora 和 RHEL 上:
$ sudo dnf install ImageMagick
在 Ubuntu 和 Debian 上:
$ sudo apt install ImageMagick
在 BSD 上,使用 ports 或者 pkgsrc 安裝。在 Mac 上,使用 Homebrew 或者 MacPorts 安裝。
在你安裝了 ImageMagick 之后,你就擁有一系列可以用來(lái)操作圖片的新命令了。
為你將要?jiǎng)?chuàng)建的文件建立一個(gè)目標(biāo)文件夾:
$ mkdir tmp
使用下面的循環(huán)可以將每張圖片減小至原來(lái)大小的 33%。
$ for f in * ; do convert $f -scale 33% tmp/$f ; done
然后就可以在 tmp 文件夾中看到已經(jīng)縮小了的照片了。
你可以在循環(huán)體中使用任意數(shù)量的命令,因此如果你需要對(duì)一批文件進(jìn)行復(fù)雜的操作,可以將你的命令放在一個(gè) for 循環(huán)的 do 和 done 語(yǔ)句之間。例如,假設(shè)你希望將所有處理過(guò)的圖片拷貝至你的網(wǎng)站所托管的圖片文件夾并且在本地系統(tǒng)移除這些文件:
$ for f in * ; doconvert $f -scale 33% tmp/$fscp -i seth_web tmp/$f seth@example.com:~/public_htmltrash tmp/$f ;done
你的計(jì)算機(jī)會(huì)對(duì) for 循環(huán)中處理的每一個(gè)文件自動(dòng)的執(zhí)行 3 條命令。這意味著假如你僅僅處理 10 張圖片,也會(huì)省下輸入 30 條指令和更多的時(shí)間。
限制你的循環(huán)
一個(gè)循環(huán)常常不需要處理所有文件。在示例文件夾中,你可能需要處理的只是 JPEG 文件:
$ for f in *.jpg ; do convert $f -scale 33% tmp/$f ; done$ ls -m tmpcat.jpg, otago.jpg
或者,你希望重復(fù)特定次數(shù)的某個(gè)操作而不僅僅只處理文件。for 循環(huán)的變量的值是被你賦給它的(不管何種類型的)數(shù)據(jù)所決定的,所以你可以創(chuàng)建一個(gè)循環(huán)遍歷數(shù)字而不只是文件:
$ for n in {0..4}; do echo $n ; done01234
更多循環(huán)
現(xiàn)在你了解的知識(shí)已經(jīng)足夠用來(lái)創(chuàng)建自己的循環(huán)體了。直到你對(duì)循環(huán)非常熟悉之前,盡可能的在需要處理的文件的副本上進(jìn)行操作。使用內(nèi)置的保護(hù)措施可以預(yù)防損壞自己的數(shù)據(jù)和制造不可復(fù)現(xiàn)的錯(cuò)誤,例如偶然將一個(gè)文件夾下的所有文件重命名為同一個(gè)名字,就可能會(huì)導(dǎo)致他們的相互覆蓋。
更進(jìn)一步的 for 循環(huán)話題,請(qǐng)繼續(xù)閱讀。
不是所有的 shell 都是 Bash
關(guān)鍵字 for 是內(nèi)置在 Bash shell 中的。許多類似的 shell 會(huì)使用和 Bash 同樣的關(guān)鍵字和語(yǔ)法,但是也有某些 shell ,比如 tcsh,使用不同的關(guān)鍵字,例如 foreach。
tcsh 的語(yǔ)法與 Bash 類似,但是它更為嚴(yán)格。例如在下面的例子中,不要在你的終端的第 2、3 行鍵入 foreach? 。它只是提示你仍處在構(gòu)建循環(huán)的過(guò)程中。
$ foreach f (*)foreach? file $fforeach? endcat.jpg: JPEG image data, EXIF standard 2.2design_maori.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlacedotago.jpg: JPEG image data, EXIF standard 2.2waterfall.png: PNG image data, 4608 x 2592, 8-bit/color RGB, non-interlaced
在 tcsh 中,foreach 和 end 都必須單獨(dú)的在一行中出現(xiàn)。因此你不能像 Bash 或者其他類似的 shell 一樣只使用一行命令創(chuàng)建一個(gè) for 循環(huán)。
for 循環(huán)與 find 命令
理論上,你可能會(huì)用到不支持 for 循環(huán)的 shell,或者你只是更想使用其他命令的一些特性來(lái)完成和循環(huán)一樣的工作。
使用 find 命令是另一個(gè)實(shí)現(xiàn) for 循環(huán)功能的途徑。這個(gè)命令提供了多種方法來(lái)定義循環(huán)中包含哪些文件的范圍以及并行處理的選項(xiàng)。
find 命令顧名思義就是幫助你查詢存儲(chǔ)在硬盤里的文件。它的用法很簡(jiǎn)單:提供一個(gè)你希望它查詢的位置的路徑,接著 find 就會(huì)查詢這個(gè)路徑下面的所有文件和文件夾。
$ find .../cat.jpg./design_maori.png./otago.jpg./waterfall.png
你可以通過(guò)添加名稱的某些部分來(lái)過(guò)濾搜索結(jié)果:
$ find . -name "*jpg"./cat.jpg./otago.jpg
find 命令非常好的地方在于你可以通過(guò) -exec 參數(shù)標(biāo)志將它查詢到的每一個(gè)文件放入循環(huán)中。例如,只對(duì)存放在你的 example 文件夾下的 PNG 圖片進(jìn)行體積壓縮操作:
$ find . -name "*png" -exec convert {} -scale 33% tmp/{} \;$ ls -m tmpdesign_maori.png, waterfall.png
在 -exec 短語(yǔ)中,括號(hào) {} 表示的是 find 正在處理的條目(換句話說(shuō),每一個(gè)被找到的以 PNG 結(jié)尾的文件)。-exec 短語(yǔ)必須使用分號(hào)結(jié)尾,但是 Bash 中常常也會(huì)使用分號(hào)。為了解決這個(gè)二義性問(wèn)題,你的 結(jié)束符 可以使用反斜杠加上一個(gè)分號(hào)(\;),使得 find 命令可以知道這個(gè)結(jié)束符是用來(lái)標(biāo)識(shí)自己結(jié)束使用的。
find 命令的操作非常棒,某些情況下它甚至可以表現(xiàn)得更棒。比如說(shuō),在一個(gè)新的進(jìn)程中使用同一條命令查找 PNG 文件,你可能就會(huì)得到一些錯(cuò)誤信息:
$ find . -name "*png" -exec convert {} -flip -flop tmp/{} \;convert: unable to open image `tmp/./tmp/design_maori.png':No such file or directory @ error/blob.c/OpenBlob/2643....
看起來(lái) find 不只是定位了當(dāng)前文件夾(.)下的所有 PNG 文件,還包括已經(jīng)處理并且存儲(chǔ)到了 tmp 下的文件。在一些情況下,你可能希望 find 查詢當(dāng)前文件夾下再加上其子文件夾下的所有文件。find 命令是一個(gè)功能強(qiáng)大的遞歸工具,特別體現(xiàn)在處理一些文件結(jié)構(gòu)復(fù)雜的情境下(比如用來(lái)放置存滿了音樂(lè)人音樂(lè)專輯的文件夾),同時(shí)你也可以使用 -maxdepth 選項(xiàng)來(lái)限制最大的遞歸深度。
只在當(dāng)前文件夾下查找 PNG 文件(不包括子文件夾):
$ find . -maxdepth 1 -name "*png"
上一條命令的最大深度再加 1 就可以查找和處理當(dāng)前文件夾及下一級(jí)子文件夾下面的文件:
$ find . -maxdepth 2 -name "*png"
find 命令默認(rèn)是查找每一級(jí)文件夾。
循環(huán)的樂(lè)趣與收益
你使用的循環(huán)越多,你就可以越多的省下時(shí)間和力氣,并且可以應(yīng)對(duì)龐大的任務(wù)。雖然你只是一個(gè)用戶,但是通過(guò)使用循環(huán),可以使你的計(jì)算機(jī)完成困難的任務(wù)。
你可以并且應(yīng)該就像使用其他的命令一樣使用循環(huán)。在你需要重復(fù)處理單個(gè)或多個(gè)文件時(shí),盡可能的使用這個(gè)命令。無(wú)論如何,這也算是一項(xiàng)需要被嚴(yán)肅對(duì)待的編程活動(dòng),因此如果你需要在一些文件上完成復(fù)雜的任務(wù),你應(yīng)該多花點(diǎn)時(shí)間在規(guī)劃自己的工作流上面。如果你可以在一份文件上完成你的工作,接下來(lái)將操作包裝進(jìn) for 循環(huán)里就相對(duì)簡(jiǎn)單了,這里面唯一的“編程”的需要只是理解變量是如何工作的并且進(jìn)行充分的規(guī)劃工作將已處理過(guò)的文件和未處理過(guò)的文件分開(kāi)。經(jīng)過(guò)一段時(shí)間的練習(xí),你就可以從一名 Linux 用戶升級(jí)成一位知道如何使用循環(huán)的 Linux 用戶,所以開(kāi)始讓計(jì)算機(jī)為你工作吧!





























