天天看點

Bash Shell 腳本新手指南(二)

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 進行搜尋

管道鍵在鍵盤上位于Enter鍵上方,可以在英文狀态下按

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

指令的完整結果通過管道傳送到搜尋字元串

learn

grep

指令中。你可以将管道指令想象成一個過濾器。先運作一個指令(本例中為

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中國榮譽推出

繼續閱讀