Linux中國

Bash Shell 腳本新手指南(二)

歡迎來到面向初學者的 Bash Shell 腳本知識第二部分。本篇將就 Bash 腳本一些更獨特的方面進行深入探討。我們會用到一些 上篇 中已經熟悉的命令(如果遇到新命令,會給出講解),進而涵蓋一些標準輸出、標準輸入、標準錯誤、「管道」和數據重定向的相關知識。

使用 # 添加註釋

隨著腳本變得愈加複雜和實用,我們需要添加註釋,以便記住程序在做什麼。如果與其他人分享你的腳本,注釋也將幫助他們理解思考過程,以及更好理解你的腳本實現的功能。想一想上篇文章中的數學方程,我們在新版腳本中添加了一些注釋。注意,在 learnToScript.sh 文件(如下所示)中,注釋是前面帶有 # 號的行。當腳本運行時,這些注釋行並不會出現。

#!/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 可以在輸入文件中搜索可以匹配指定模式的行。默認情況下,grep 會輸出相應的匹配行。

https://www.gnu.org/software/grep/

Paul W. Frields 在 《Fedora 雜誌》上的文章很好地介紹了關於 grep 的知識。

命令行快速小技巧:使用 grep 進行搜索

管道鍵在鍵盤上位於回車鍵上方,可以在英文狀態下按 Shift + 輸入。

現在你已經略微熟悉了 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 命令的完整結果通過管道傳送到搜索字元串 learngrep 命令中。你可以將管道命令想像成一個過濾器。先運行一個命令(本例中為 ls -l,結果會給出目錄中的文件),這些結果通過管道命令給到 grep,後者會在其中搜索 learn,並且只顯示符合條件的目標行。

下面再看一個例子以鞏固相關知識。less 命令可以讓用戶查看超出一個屏幕尺寸的命令結果。以下是命令手冊頁中關於 less 的簡要說明。

less 是一個類似於 more 的程序,但它允許在文件中向後或向前進行翻頁移動。此外,less 不必在開始之前讀取整個輸入文件,因此對於大型輸入文件而言,它比 vi 等文本編輯器啟動更快。該命令較少使用 termcap(或某些系統上的 terminfo),因此可以在各種終端上運行。甚至還在一定程度上支持用於硬拷貝終端的埠。(在硬拷貝終端上,顯示在屏幕頂部的行會以插入符號為前綴。)

Fedora 34 手冊頁

下面讓我們看看管道命令和 less 命令結合使用會是什麼樣子。

[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
:
:

為便於閱讀,此處對結果進行了修剪。用戶可以使用鍵盤上的箭頭鍵向上或向下滾動,進而控制顯示。如果使用命令行,結果超出屏幕的話,用戶可能會看不到結果的開頭行。要退出 less 屏幕,只需點擊 q 鍵。

標準輸出(stdout)重定向 >、>>、1>、1>>

>>> 符號之前的命令輸出結果,會被寫入到緊跟的文件名對應的文件中。>1> 具有相同的效果,因為 1 就代表著標準輸出。如果不顯式指定 1,則默認為標準輸出。>>1>> 將數據附加到文件的末尾。使用 >>> 時,如果文件不存在,則會創建對應文件。

例如,如果你想查看 ping 命令的輸出,以查看它是否丟棄了數據包。與其關注控制台,不如將輸出結果重定向到文件中,這樣你就可以稍後再回來查看數據包是否被丟棄。下面是使用 > 的重定向測試。

[zexcon ~]$ ls -l ~ > learnToScriptOutput

該命令會獲取本應輸出到終端的結果(~ 代表家目錄),並將其重定向到 learnToScriptOutput 文件。注意,我們並未手動創建 learnToScriptOutput,系統會自動創建該文件。

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

標準錯誤(stderr)重定向 2>2>>

>>> 符號之前命令的錯誤信息輸出,會被寫入到緊跟的文件名對應的文件中。2>2>> 具有相同的效果,但 2>> 是將數據追加到文件末尾。你可能會想,這有什麼用?不妨假象一下用戶只想捕獲錯誤信息的場景,然後你就會意識到 2>2>> 的作用。數字 2 表示本應輸出到終端的標準錯誤信息輸出。現在我們試著追蹤一個不存在的文件,以試試這個知識點。

[zexcon ~]$ ls -l /etc/invalidTest 2> learnToScriptOutputError

這會生成錯誤信息,並將錯誤信息重定向輸入到 learnToScriptOutputError 文件中。

ls: cannot access '/etc/invalidTest': No such file or directory

所有輸出重定向 &>、&>>、|&

如果你不想將標準輸出(stdout)和標準錯誤信息(stderr)寫入不同的文件,那麼在 Bash 5 中,你可以使用 &> 將標準輸出和標準錯誤重定向到同一個文件,或者使用 &>> 追加到文件末尾。

[zexcon ~]$ ls -l ~ &>> learnToScriptAllOutput
[zexcon ~]$ ls -l /etc/invalidTest &>> learnToScriptAllOutput

運行這些命令後,兩者的輸出都會進入同一個文件中,而不會區分是錯誤信息還是標準輸出。

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

如果你直接使用命令行操作,並希望將所有結果通過管道傳輸到另一個命令,可以選擇使用 |& 實現。

[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 ~]$

標準輸入(stdin)

在本篇和上篇文章中,我們已經多次使用過標準輸入(stdin),因為在每次使用鍵盤輸入時,我們都在使用標準輸入。為了區別通常意義上的「鍵盤即標準輸入」,這次我們嘗試在腳本中使用 read 命令。下面的腳本中就使用了 read 命令,字面上就像「讀取標準輸入」。

#!/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"

這個示例通過標準輸出給出提示,提醒用戶輸入信息,然後從標準輸入(鍵盤)獲取信息,使用 read 將其存儲在 name 變數中,並通過標準輸出顯示出 name 中的值。

[zexcon@fedora ~]$ ./learnToScript.sh
Please enter your name.
zexcon
With standard input you have told me your name is: zexcon
[zexcon@fedora ~]$

在腳本中使用

現在我們把學到的東西放入腳本中,學習一下如何實際應用。下面是增加了幾行後的新版本 learnToScript.sh 文件。它用追加的方式將標準輸出、標準錯誤信息,以及兩者混合後的信息,分別寫入到三個不同文件。它將標準輸出寫入 learnToScriptStandardOutput,標準錯誤信息寫入 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

腳本在同一目錄中創建了三個文件。命令 echo 故意輸入錯誤(LCTT 譯註:缺少了字母 h)以產生錯誤信息。如果查看三個文件,你會在 learnToScriptStandardOutput 中看到一條信息,在 learnToScriptStandardError 中看到一條信息,在 learnToScriptAllOutput 中看到兩條信息。另外,該腳本還會再次提示輸入的 name 值,再將其寫入 learnToScriptStandardOutput 中。

結語

至此你應該能夠明確,可以在命令行中執行的操作,都可以在腳本中執行。在編寫可能供他人使用的腳本時,文檔非常重要。如果繼續深入研究腳本,標準輸出會顯得更有意義,因為你將會控制它們的生成。在腳本中,你可以與命令行中操作時應用相同的內容。下一篇文章我們會討論函數、循環,以及在此基礎上進一步構建的結構。

via: https://fedoramagazine.org/bash-shell-scripting-for-beginners-part-2/

作者:Matthew Darnell 選題:lujun9972 譯者:unigeorge 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的電子郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國