Bash Shell 腳本新手指南(二)
歡迎來到面向初學(xué)者的 Bash Shell 腳本知識第二部分。本篇將就 Bash 腳本一些更獨特的方面進(jìn)行深入探討。我們會用到一些 ??上篇?? 中已經(jīng)熟悉的命令(如果遇到新命令,會給出講解),進(jìn)而涵蓋一些標(biāo)準(zhǔn)輸出、標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)錯誤、“管道”和數(shù)據(jù)重定向的相關(guān)知識。
使用 # 添加注釋
隨著腳本變得愈加復(fù)雜和實用,我們需要添加注釋,以便記住程序在做什么。如果與其他人分享你的腳本,注釋也將幫助他們理解思考過程,以及更好理解你的腳本實現(xiàn)的功能。想一想上篇文章中的數(shù)學(xué)方程,我們在新版腳本中添加了一些注釋。注意,在 ??learnToScript.sh?
?? 文件(如下所示)中,注釋是前面帶有 ??#?
? 號的行。當(dāng)腳本運行時,這些注釋行并不會出現(xiàn)。
#!/bin/bash
#Let's pick up from our last article. We
#learned how to use mathematical equations
#in bash scripting.
echo $((5+3))
echo $((5-3))
echo $((5*3))
echo $((5/3))
[zexcon ~]$ ./learnToScript.sh
8
2
15
1
管道符 |
我們將使用另一個名為 ??grep?
? 的工具來介紹管道運算符。
?
?grep?
?? 可以在輸入文件中搜索可以匹配指定模式的行。默認(rèn)情況下,??grep?
? 會輸出相應(yīng)的匹配行。??https://www.gnu.org/software/grep/??
Paul W. Frields 在 《Fedora 雜志》上的文章很好地介紹了關(guān)于 grep 的知識。
??命令行快速小技巧:使用 grep 進(jìn)行搜索??
管道鍵在鍵盤上位于回車鍵上方,可以在英文狀態(tài)下按 ??Shift + \?
? 輸入。
現(xiàn)在你已經(jīng)略微熟悉了 ??grep?
??,接下來看一個使用管道命令的示例。在命令行輸入 ??ls -l | grep learn?
?。
[zexcon ~]$ ls -l | grep learn
-rwxrw-rw-. 1 zexcon zexcon 70 Sep 17 10:10 learnToScript.sh
通常 ??ls -l?
?? 命令會在屏幕上顯示文件列表。這里 ??ls -l?
?? 命令的完整結(jié)果通過管道傳送到搜索字符串 ??learn?
?? 的 ??grep?
?? 命令中。你可以將管道命令想象成一個過濾器。先運行一個命令(本例中為 ??ls -l?
??,結(jié)果會給出目錄中的文件),這些結(jié)果通過管道命令給到 ??grep?
??,后者會在其中搜索 ??learn?
?,并且只顯示符合條件的目標(biāo)行。
下面再看一個例子以鞏固相關(guān)知識。??less?
?? 命令可以讓用戶查看超出一個屏幕尺寸的命令結(jié)果。以下是命令手冊頁中關(guān)于 ??less?
? 的簡要說明。
?
?less?
?? 是一個類似于 ??more?
?? 的程序,但它允許在文件中向后或向前進(jìn)行翻頁移動。此外,??less?
?? 不必在開始之前讀取整個輸入文件,因此對于大型輸入文件而言,它比 ??vi?
? 等文本編輯器啟動更快。該命令較少使用 termcap(或某些系統(tǒng)上的 terminfo),因此可以在各種終端上運行。甚至還在一定程度上支持用于硬拷貝終端的端口。(在硬拷貝終端上,顯示在屏幕頂部的行會以插入符號為前綴。)Fedora 34 手冊頁
下面讓我們看看管道命令和 ??less?
? 命令結(jié)合使用會是什么樣子。
[zexcon ~]$ ls -l /etc | less
total 1504
drwxr-xr-x. 1 root root 126 Jul 7 17:46 abrt
-rw-r--r--. 1 root root 18 Jul 7 16:04 adjtime
-rw-r--r--. 1 root root 1529 Jun 23 2020 aliases
drwxr-xr-x. 1 root root 70 Jul 7 17:47 alsa
drwxr-xr-x. 1 root root 14 Apr 23 05:58 cron.d
drwxr-xr-x. 1 root root 0 Jan 25 2021 cron.daily
:
:
為便于閱讀,此處對結(jié)果進(jìn)行了修剪。用戶可以使用鍵盤上的箭頭鍵向上或向下滾動,進(jìn)而控制顯示。如果使用命令行,結(jié)果超出屏幕的話,用戶可能會看不到結(jié)果的開頭行。要退出 ??less?
?? 屏幕,只需點擊 ??q?
? 鍵。
標(biāo)準(zhǔn)輸出(stdout)重定向 >、>>、1>、1>>
??>?
?? 或 ??>>?
?? 符號之前的命令輸出結(jié)果,會被寫入到緊跟的文件名對應(yīng)的文件中。??>?
?? 和 ??1>?
?? 具有相同的效果,因為 ??1?
?? 就代表著標(biāo)準(zhǔn)輸出。如果不顯式指定 ??1?
??,則默認(rèn)為標(biāo)準(zhǔn)輸出。??>>?
?? 和 ??1>>?
?? 將數(shù)據(jù)附加到文件的末尾。使用 ??>?
?? 或 ??>>?
? 時,如果文件不存在,則會創(chuàng)建對應(yīng)文件。
例如,如果你想查看 ??ping?
?? 命令的輸出,以查看它是否丟棄了數(shù)據(jù)包。與其關(guān)注控制臺,不如將輸出結(jié)果重定向到文件中,這樣你就可以稍后再回來查看數(shù)據(jù)包是否被丟棄。下面是使用 ??>?
? 的重定向測試。
[zexcon ~]$ ls -l ~ > learnToScriptOutput
該命令會獲取本應(yīng)輸出到終端的結(jié)果(??~?
?? 代表家目錄),并將其重定向到 ??learnToScriptOutput?
?? 文件。注意,我們并未手動創(chuàng)建 ??learnToScriptOutput?
?,系統(tǒng)會自動創(chuàng)建該文件。
total 128
drwxr-xr-x. 1 zexcon zexcon 268 Oct 1 16:02 Desktop
drwxr-xr-x. 1 zexcon zexcon 80 Sep 16 08:53 Documents
drwxr-xr-x. 1 zexcon zexcon 0 Oct 1 15:59 Downloads
-rw-rw-r--. 1 zexcon zexcon 685 Oct 4 16:00 learnToScriptAllOutput
-rw-rw-r--. 1 zexcon zexcon 23 Oct 4 12:42 learnToScriptInput
-rw-rw-r--. 1 zexcon zexcon 0 Oct 4 16:42 learnToScriptOutput
-rw-rw-r--. 1 zexcon zexcon 52 Oct 4 16:07 learnToScriptOutputError
-rwxrw-rw-. 1 zexcon zexcon 477 Oct 4 15:01 learnToScript.sh
drwxr-xr-x. 1 zexcon zexcon 0 Jul 7 16:04 Videos
標(biāo)準(zhǔn)錯誤(stderr)重定向 ??2>?
??、??2>>?
?
??>?
?? 或 ??>>?
?? 符號之前命令的錯誤信息輸出,會被寫入到緊跟的文件名對應(yīng)的文件中。??2>?
?? 和 ??2>>?
?? 具有相同的效果,但 ??2>>?
?? 是將數(shù)據(jù)追加到文件末尾。你可能會想,這有什么用?不妨假象一下用戶只想捕獲錯誤信息的場景,然后你就會意識到 ??2>?
?? 或 ??2>>?
?? 的作用。數(shù)字 ??2?
? 表示本應(yīng)輸出到終端的標(biāo)準(zhǔn)錯誤信息輸出?,F(xiàn)在我們試著追蹤一個不存在的文件,以試試這個知識點。
[zexcon ~]$ ls -l /etc/invalidTest 2> learnToScriptOutputError
這會生成錯誤信息,并將錯誤信息重定向輸入到 ??learnToScriptOutputError?
? 文件中。
ls: cannot access '/etc/invalidTest': No such file or directory
所有輸出重定向 &>、&>>、|&
如果你不想將標(biāo)準(zhǔn)輸出(??stdout?
??)和標(biāo)準(zhǔn)錯誤信息(??stderr?
??)寫入不同的文件,那么在 Bash 5 中,你可以使用 ??&>?
?? 將標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯誤重定向到同一個文件,或者使用 ??&>>?
? 追加到文件末尾。
[zexcon ~]$ ls -l ~ &>> learnToScriptAllOutput
[zexcon ~]$ ls -l /etc/invalidTest &>> learnToScriptAllOutput
運行這些命令后,兩者的輸出都會進(jìn)入同一個文件中,而不會區(qū)分是錯誤信息還是標(biāo)準(zhǔn)輸出。
total 128
drwxr-xr-x. 1 zexcon zexcon 268 Oct 1 16:02 Desktop
drwxr-xr-x. 1 zexcon zexcon 80 Sep 16 08:53 Documents
drwxr-xr-x. 1 zexcon zexcon 0 Oct 1 15:59 Downloads
-rw-rw-r--. 1 zexcon zexcon 685 Oct 4 16:00 learnToScriptAllOutput
-rw-rw-r--. 1 zexcon zexcon 23 Oct 4 12:42 learnToScriptInput
-rw-rw-r--. 1 zexcon zexcon 0 Oct 4 16:42 learnToScriptOutput
-rw-rw-r--. 1 zexcon zexcon 52 Oct 4 16:07 learnToScriptOutputError
-rwxrw-rw-. 1 zexcon zexcon 477 Oct 4 15:01 learnToScript.sh
drwxr-xr-x. 1 zexcon zexcon 0 Jul 7 16:04 Videos
ls: cannot access '/etc/invalidTest': No such file or directory
如果你直接使用命令行操作,并希望將所有結(jié)果通過管道傳輸?shù)搅硪粋€命令,可以選擇使用 ??|&?
? 實現(xiàn)。
[zexcon ~]$ ls -l |& grep learn
-rw-rw-r--. 1 zexcon zexcon 1197 Oct 18 09:46 learnToScriptAllOutput
-rw-rw-r--. 1 zexcon zexcon 343 Oct 14 10:47 learnToScriptError
-rw-rw-r--. 1 zexcon zexcon 0 Oct 14 11:11 learnToScriptOut
-rw-rw-r--. 1 zexcon zexcon 348 Oct 14 10:27 learnToScriptOutError
-rwxr-x---. 1 zexcon zexcon 328 Oct 18 09:46 learnToScript.sh
[zexcon ~]$
標(biāo)準(zhǔn)輸入(stdin)
在本篇和上篇文章中,我們已經(jīng)多次使用過標(biāo)準(zhǔn)輸入(stdin),因為在每次使用鍵盤輸入時,我們都在使用標(biāo)準(zhǔn)輸入。為了區(qū)別通常意義上的“鍵盤即標(biāo)準(zhǔn)輸入”,這次我們嘗試在腳本中使用 ??read?
?? 命令。下面的腳本中就使用了 ??read?
? 命令,字面上就像“讀取標(biāo)準(zhǔn)輸入”。
#!/bin/bash
#Here we are asking a question to prompt the user for standard input. i.e.keyboard
echo 'Please enter your name.'
#Here we are reading the standard input and assigning it to the variable name with the read command.
read name
#We are now going back to standard output, by using echo and printing your name to the command line.
echo "With standard input you have told me your name is: $name"
這個示例通過標(biāo)準(zhǔn)輸出給出提示,提醒用戶輸入信息,然后從標(biāo)準(zhǔn)輸入(鍵盤)獲取信息,使用 ??read?
?? 將其存儲在 ??name?
?? 變量中,并通過標(biāo)準(zhǔn)輸出顯示出 ??name?
? 中的值。
[zexcon@fedora ~]$ ./learnToScript.sh
Please enter your name.
zexcon
With standard input you have told me your name is: zexcon
[zexcon@fedora ~]$
在腳本中使用
現(xiàn)在我們把學(xué)到的東西放入腳本中,學(xué)習(xí)一下如何實際應(yīng)用。下面是增加了幾行后的新版本 ??learnToScript.sh?
?? 文件。它用追加的方式將標(biāo)準(zhǔn)輸出、標(biāo)準(zhǔn)錯誤信息,以及兩者混合后的信息,分別寫入到三個不同文件。它將標(biāo)準(zhǔn)輸出寫入 ??learnToScriptStandardOutput?
??,標(biāo)準(zhǔn)錯誤信息寫入 ??learnToScriptStandardError?
??,二者共同都寫入 ??learnToScriptAllOutput?
? 文件。
#!/bin/bash
#As we know this article is about scripting. So let's
#use what we learned in a script.
#Let's get some information from the user and add it to our scripts with stanard input and read
echo "What is your name? "
read name
#Here standard output directed to append a file to learnToScirptStandardOutput
echo "$name, this will take standard output with append >> and redirect to learnToScriptStandardOutput." 1>> learnToScriptStandardOutput
#Here we are taking the standard error and appending it to learnToScriptStandardError but to see this we need to #create an error.
eco "Standard error with append >> redirect to learnToScriptStandardError." 2>> learnToScriptStandardError
#Here we are going to create an error and a standard output and see they go to the same place.
echo "Standard output with append >> redirect to learnToScriptAllOutput." &>> learnToScriptAllOutput
eco "Standard error with append >> redirect to learnToScriptAllOutput." &>> learnToScriptAllOutput
腳本在同一目錄中創(chuàng)建了三個文件。命令 ??echo?
?? 故意輸入錯誤(LCTT 譯注:缺少了字母 h)以產(chǎn)生錯誤信息。如果查看三個文件,你會在 ??learnToScriptStandardOutput?
?? 中看到一條信息,在 ??learnToScriptStandardError?
?? 中看到一條信息,在 ??learnToScriptAllOutput?
?? 中看到兩條信息。另外,該腳本還會再次提示輸入的 ??name?
?? 值,再將其寫入 ??learnToScriptStandardOutput?
? 中。
結(jié)語
至此你應(yīng)該能夠明確,可以在命令行中執(zhí)行的操作,都可以在腳本中執(zhí)行。在編寫可能供他人使用的腳本時,文檔非常重要。如果繼續(xù)深入研究腳本,標(biāo)準(zhǔn)輸出會顯得更有意義,因為你將會控制它們的生成。在腳本中,你可以與命令行中操作時應(yīng)用相同的內(nèi)容。下一篇文章我們會討論函數(shù)、循環(huán),以及在此基礎(chǔ)上進(jìn)一步構(gòu)建的結(jié)構(gòu)。