您當前的位置:首頁 > 旅遊

還不懂shell指令碼核心?這一篇就夠了!

作者:由 滌生大資料 發表于 旅遊時間:2022-12-13

前言:

本章討論編寫 shell指令碼的基礎知識。在開始編寫自己的shell指令碼前,你必須瞭解的基本概念都在這裡。

一、多個shell命令的使用

shell指令碼的核心在於輸入多個命令並處理每個命令的結果,而且有時候需要將一個命令的結果傳給

另一個命令。shell可以將命令串起來,依次執行完成。要依次將兩個命令一起執行,可以 把它們放在同一行中,彼此間用分號(;)隔開來。

還不懂shell指令碼核心?這一篇就夠了!

這就是一個最簡單的shell指令碼,運行了兩個shell命令,who命令先執行,輸出了當前是誰登入了系統,而後運行了whoami,輸出的是當前有效使用者名稱。使用這個方式可以執行多個命令,它們都是以此序列的。

二、構建一個shell檔案

構建一個shell檔案,最簡單的理解就是將類似上述的命令放在一個文字檔案裡,文字檔案的核心開頭是:

#!/bin/bash

#內容解釋:

在通常的shell指令碼中,井號(#)用作註釋行。shell並不會處理shell指令碼中的註釋行。而, shell指令碼檔案的第一行是個例外,#後面的驚歎號會告訴shell用哪個shell來執行指令碼

該行內容必須放在文字的第一行,表示使用的shell型別;本文以常用的bash為例,更多了型別的shell可以參考此文。

在第一行的內容後面,就可以寫入你要執行的shell命令了,可以都寫在一行,用分號隔開,但是一般情況下,為了美觀和更高的辨識度,我們選擇一行寫一個命令,加上一個回車符,在輸入另一個命令。好比下圖:

還不懂shell指令碼核心?這一篇就夠了!

推薦格式:

還不懂shell指令碼核心?這一篇就夠了!

需要說明的是,你可以在文字中用“#”來註釋你的內容,這樣這些被註釋的內容,shell就不會識別和執行了,一般我們會在指令碼中寫一些說明性的描述,這時需要用到“#”。如下圖:

還不懂shell指令碼核心?這一篇就夠了!

上述可以是一個完整的shell指令碼了,可以直接儲存為指令碼檔案test1,但是此時我們如果直接執行test1檔案的話,還是不能達到效果的,會提示 command not found,這裡就需要提到shell裡的PATH環境變數的概念。shell 命令的查詢都是透過環境變數的。

還不懂shell指令碼核心?這一篇就夠了!

我們可以檢視當前主機的環境變數:

還不懂shell指令碼核心?這一篇就夠了!

此時我們的test1命令並沒有生效,如果我們想要使其神效,可以採用這兩個方式:

將shell指令碼檔案所處的目錄新增到PATH環境變數中;

在提示符中用絕對或相對檔案路徑來引用shell指令碼檔案;

經驗提示:

在centos Linux發行版中,有的會將$HOME/bin目錄新增進了PATH環境變數。它在每個使用者的HOME目錄下提供了一個存放檔案的地方,shell可以在那裡查詢要執行的命令;

在本文中,我們將用第二種方式將指令碼檔案的確切位置告訴shell。記住,為了引用當前 目錄下的檔案,可以在shell中使用單點運算子,如下圖:

還不懂shell指令碼核心?這一篇就夠了!

核心說明:

執行時我們會發現還是沒有執行成功,此時可以看到終端列印了 “

Permission denied

”,這報錯大家一定要熟悉起來,因為在以後的工作中,我們可能會遇到很多這種報錯,遇到這個問題我們的第一反應就應該是想到,有些檔案或者目錄,我們當前的使用者是沒有相關的許可權導致。正如test1檔案,我們當前的test1使用者是沒有執行許可權的,所以我們需要做的就是使用chmod 給檔案新增對應的許可權。

還不懂shell指令碼核心?這一篇就夠了!

chmod u+x test1 新增許可權後:

還不懂shell指令碼核心?這一篇就夠了!

此時指令碼檔案就可以正常執行了。

還不懂shell指令碼核心?這一篇就夠了!

實戰解說:

工作中我們建立的指令碼檔案,一般都是用

.sh 結尾

的,這個是給我們電腦的使用者來識別用的,這樣我們就可以一眼識別這個檔案就是一個shell 指令碼檔案,比如上面的test1檔案,我們通常是命名為test1。sh的。而且執行這個檔案的時候,我們可以有一個更簡單的方式,指令碼對應的sh或bash來執行,好比上面的。/test1 我們可以更換為 bash test1,會有同樣的效果。

還不懂shell指令碼核心?這一篇就夠了!

三、終端列印訊息

很多時候shell都會輸出一定的內容到終端,我們如果也想在指令碼中輸出一些內容到終端顯示,告訴執行指令碼的人,這個指令碼在執行哪些功能,這個時候我們就需要用到

echo

命令來輔助。

最簡單的輸出如下:

還不懂shell指令碼核心?這一篇就夠了!

echo 命令會將跟在它後面的字串列印到終端螢幕。

注意,預設情況下,不需要使用引號將要顯示的文字字串劃定出來。但有時在字串中出現引號的話就比較麻煩了。

如下圖:

還不懂shell指令碼核心?這一篇就夠了!

此時我們如果想要引號也輸出在終端的話,需要這樣做:

echo "Let's see if this'll work"

還不懂shell指令碼核心?這一篇就夠了!

核心總結:

echo命令可用單引號或雙引號來劃定文字字串。如果在字串中用到了它們,你需要在

文字中使用其中一種引號,而用另外一種來將字串劃定起來。

此時我們就可以在指令碼檔案中任意位置使用echo來輸出我們打算輸出的內容了。如下圖:

還不懂shell指令碼核心?這一篇就夠了!

常用的組合命令引數:

-n 不換行輸出

還不懂shell指令碼核心?這一篇就夠了!

-e 處理特殊字元

\a 發出警告聲;

\b 刪除前一個字元;

\c 最後不加上換行符號;

\f 換行但游標仍舊停留在原來的位置;

\n 換行且游標移至行首;

\r 游標移至行首,但不換行;

\t 插入tab;

\v 與\f相同;

\ 插入\字元;

\nnn 插入nnn(八進位制)所代表的ASCII字元;

還不懂shell指令碼核心?這一篇就夠了!

實戰解說:

在實際的使用中,我們通常也會使用echo 配合>>將內容追加到文字檔案中,如下圖:

還不懂shell指令碼核心?這一篇就夠了!

四、變數的使用

有些時候我們會需要在shell命令使用 其他資料來處理資訊。這可以透過變數來實現。變數允許臨時性地將資訊儲存在shell指令碼中, 以便和指令碼中的其他命令一起使用。

4。1 環境變數

shell維護著一組環境變數,用來記錄特定的系統資訊。比如系統的名稱、登入到系統上的用 戶名、使用者的系統ID(也稱為UID)、使用者的預設主目錄以及shell查詢程式的搜尋徑。可以用 set命令來顯示一份完整的當前環境變數列表。

如下圖:

還不懂shell指令碼核心?這一篇就夠了!

在指令碼中,變數的使用格式是:$變數名稱

下面是變數在指令碼中的使用,可以看到

還不懂shell指令碼核心?這一篇就夠了!

實戰解說:

如上文中的$HOME,我們一般還可以寫成${HOME} 這兩者的效果是等同的,而且需要注意的是,我們在$符號之前不能只是\,這樣變數就會失效了,另一個需要注意的是,當你的變數需要和一個字串連用的時候,此時一定要用{}的形式,否則變數會失效,如下圖演示:

還不懂shell指令碼核心?這一篇就夠了!

變數用{}包括起來:

還不懂shell指令碼核心?這一篇就夠了!

4。2 使用者變數

除了環境變數,shell指令碼還允許在指令碼中定義和使用自己的變數。定義變數允許臨時儲存數

據並在整個指令碼中使用,從而使shell指令碼看起來更像一個真正的計算機程式。

使用者變數可以是任何由字母、數字或下劃線組成的文字字串,長度不超過20個。使用者變數

區分大小寫,所以變數Var1和變數var1是不同的。這個小規矩經常讓指令碼程式設計初學者感到頭疼。

使用等號將值賦給使用者變數。在變數、等號和值之間不能出現空格(另一個困擾初學者的用

法)。這裡有一些給使用者變數賦值的例子。

變數示例:

var1=10

var2=-57

var3=testing

var4=“still more testing”

shell指令碼會自動決定變數值的資料型別。在指令碼的整個生命週期裡,shell指令碼中定義的變數 會一直保持著它們的值,但在shell指令碼結束時會被刪除掉。 與系統變數類似,使用者變數可透過$引用。

示例截圖:

還不懂shell指令碼核心?這一篇就夠了!

核心解說:

變數每次被引用時,都會輸出當前賦給它的值。需要記住的是,引用一個變數值時需要使 用$符,而引用變數來對其進行賦值時則不要使用美元符。看下面的例子。

演示截圖:

還不懂shell指令碼核心?這一篇就夠了!

如果沒有使用$符,shell會將變數名解釋成普通的文字字串,通常這並不是你想要的結果。

還不懂shell指令碼核心?這一篇就夠了!

4。3命令替換

shell指令碼中最有用的特性之一就是可以從命令輸出中提取資訊,並將其賦給變數。把輸出賦

給變數之後,就可以隨意在指令碼中使用了。這個特性在處理指令碼資料時尤為方便。

兩種操作方式:

反引號字元(`)

$()

核心解說:

需要注意反引號字元,這可不是用於字串的那個普通的單引號字元。由於在shell指令碼之外很 少用到,你可能甚至都不知道在鍵盤什麼地方能找到這個字元。但你必須慢慢熟悉它,因為這是 許多shell指令碼中的重要元件。提示:在美式鍵盤上,它通常和波浪線(~)位於同一鍵位。 命令替換允許你將shell命令的輸出賦給變數。儘管這看起來並不那麼重要,但它卻是指令碼編 程中的一個主要組成部分。

如下示例:

用一對反引號把整個命令列命令圍起來:

day=‘date’

使用$()格式:

day=$(date)

shell會執行命令替換符號中的命令,並將其輸出賦給變數testing。注意,賦值等號和命令

替換字元之間沒有空格。這裡有個使用普通的shell命令輸出建立變數的例子。

還不懂shell指令碼核心?這一篇就夠了!

實戰詳解:

命令替換會建立一個子shell來執行對應的命令。子shell(subshell)是由執行該指令碼的shell

所創建出來的一個獨立的子shell(child shell)。正因如此,由該子shell所執行命令是無法使用指令碼中所建立的變數的。

在命令列提示符下使用路徑。/執行命令的話,也會創建出子shell;要是執行命令的時候 不加入路徑,就不會建立子shell。如果你使用的是內建的shell命令,並不會涉及子shell。 在命令列提示符下執行指令碼時一定要留心!

五、重定向輸入和輸出

很多時候想要儲存某個命令的輸出而不僅僅只是讓它顯示在顯示器上。bash shell提供了幾 個運算子,可以將命令的輸出重定向到另一個位置(比如檔案)。重定向可以用於輸入,也可以 用於輸出,可以將檔案重定向到命令輸入。

5。1 輸出重定向

最基本的重定向將命令的輸出傳送到一個檔案中。bash shell用大於號(>)來完成這項功能:

使用格式:

command > outputfile

之前顯示器上出現的命令輸出會被儲存到指定的輸出檔案中。

還不懂shell指令碼核心?這一篇就夠了!

重定向運算子建立了一個檔案1。txt(透過預設的umask設定),並將echo命令的輸出重定向

到該檔案中。如果輸出檔案已經存在了,重定向運算子會用新的檔案資料覆蓋已有檔案。

還不懂shell指令碼核心?這一篇就夠了!

很多時候我們可能並不想覆蓋檔案原有內容,而是想要將命令的輸出追加到已有檔案中,比如在建立一個記錄系統上某個操作的日誌檔案。在這種情況下,可以用雙大於號(>>)來追加資料。

還不懂shell指令碼核心?這一篇就夠了!

可以看到,who命令產生的內容並沒有覆蓋1。txt中已有的內容,而是追加到檔案的末尾。

5。2 輸入重定向

輸入重定向和輸出重定向正好相反。輸入重定向將檔案的內容重定向到命令,而非將命令的

輸出重定向到檔案。

輸入重定向符號是小於號(<):

命令格式:

command < inputfile

一個簡單的記憶方法就是:在命令列上,命令總是在左側,而重定向符號“指向”資料流動

的方向。小於號說明資料正在從輸入檔案流向命令。

還不懂shell指令碼核心?這一篇就夠了!

wc命令可以對對資料中的文字進行計數。預設情況下,它會輸出3個值:

文字的行數

文字的詞數

文字的位元組數

透過將文字檔案重定向到wc命令,你立刻就可以得到檔案中的行、詞和位元組的計數。這個例 子說明1。txt檔案有2行、11個單詞以及83位元組。

還有另外一種輸入重定向的方法,稱為內聯輸入重定向(inline input redirection)。這種方法 無需使用檔案進行重定向,只需要在命令列中指定用於輸入重定向的資料就可以了。乍看一眼, 這可能有點奇怪,但有些應用會用到這種方式。

內聯輸入重定向符號是遠小於號(<<)。除了這個符號,你必須指定一個文字標記來劃分輸 入資料的開始和結尾。任何字串都可作為文字標記,但在資料的開始和結尾文字標記必須一致。

command << EOF

data

EOF

在命令列上使用內聯輸入重定向時,shell會用PS2環境變數中定義的次提示符(參見第6章)

來提示輸入資料。下面是它的使用情況。

還不懂shell指令碼核心?這一篇就夠了!

六、管道

透過前面的學習,我們已經知道了怎樣從檔案重定向輸入,以及重定向輸出到檔案。Shell 還有一種功能,就是可以將兩個或者多個命令(程式或者程序)連線到一起,把一個命令的輸出作為下一個命令的輸入,以這種方式連線的兩個或者多個命令就形成了

管道 ‘|’(pipe)

Linux 管道使用豎線

|

連線多個命令,這被稱為管道符。Linux 管道的具體語法格式如下:

command1 | command2

command1 | command2 [ | commandN。。。 ]

當在兩個命令之間設定管道時,管道符

|

左邊命令的輸出就變成了右邊命令的輸入。只要第一個命令向標準輸出寫入,而第二個命令是從標準輸入讀取,那麼這兩個命令就可以形成一個管道。大部分的 Linux 命令都可以用來形成管道。

核心講解:

這裡需要注意,command1 必須有正確輸出,而 command2 必須可以處理 command2 的輸出結果;而且 command2 只能處理 command1 的正確輸出結果,不能處理 command1 的錯誤資訊。

使用示例:

a。 工作中常用的就是配合grep 使用,下圖表示的意思是,將cat讀取出來的文字內容傳送到 grep 命令;

還不懂shell指令碼核心?這一篇就夠了!

b。 使用管道將 cat 命令的輸出作為 less 命令的輸入,這樣就可以將 cat 命令的輸出每次按照一個螢幕的長度顯示,這對於檢視長度大於一個螢幕的檔案內容很有幫助。

還不懂shell指令碼核心?這一篇就夠了!

c。 檢視指定程式的程序執行狀態,並將輸出重定向到檔案中。

還不懂shell指令碼核心?這一篇就夠了!

​編輯d。統計系統中當前登入的使用者數。

還不懂shell指令碼核心?這一篇就夠了!

七、執行數學運算

對任何程式語言都很重要的特性是運算元字的能力。遺憾的是,對shell指令碼來說,這

個處理過程會比較麻煩。在shell指令碼中有兩種途徑來進行數學運算。

7.1 expr

命令

expr 是 evaluate expressions 的縮寫,譯為“表示式求值”。Shell expr 是一個功能強大,並且比較複雜的命令,它除了可以實現整數計算,還可以結合一些選項對字串進行處理,例如計算字串長度、字串比較、字串匹配、字串提取等。

還不懂shell指令碼核心?這一篇就夠了!

expr 對

表示式

的格式有幾點特殊的要求:

出現在

表示式

中的運算子、數字、變數和小括號的左右兩邊至少要有一個空格,否則會報錯。

有些特殊符號必須用反斜槓

\

進行轉義(遮蔽其特殊含義),比如乘號

*

和小括號

()

,如果不用

\

轉義,那麼 Shell 會把它們誤解為正則表示式中的符號(

*

對應萬用字元,

()

對應分組)。

使用變數時要加

$

字首。

[root@bd15-21-131-161 ~]# expr 2 +3 #錯誤:加號和 3 之前沒有空格

expr: syntax error

[root@bd15-21-131-161 ~]# expr 2 + 3 #這樣才是正確的

5

[root@bd15-21-131-161 ~]# expr 4 * 5 #錯誤:乘號沒有轉義

expr: syntax error

[root@bd15-21-131-161 ~]# expr 4 \* 5 #使用 \ 轉義後才是正確的

20

[root@bd15-21-131-161 ~]# expr ( 2 + 3 ) \* 4 #小括號也需要轉義

-bash: syntax error near unexpected token `2‘

[root@bd15-21-131-161 ~]# expr \( 2 + 3 \) \* 4 #使用 \ 轉義後才是正確的

20

[root@bd15-21-131-161 ~]# n=3

[root@bd15-21-131-161 ~]# expr n + 2

expr: non-numeric argument

[root@bd15-21-131-161 ~]# expr $n + 2 #使用變數時要加 $

5

[root@bd15-21-131-161 ~]# m=7

[root@bd15-21-131-161 ~]# expr $m \* \( $n + 5 \)

56

以上是直接使用 expr 命令,計算結果會直接輸出,如果你希望將計算結果賦值給變數,那麼需要將整個表示式用反引號

``

(位於 Tab 鍵的上方)包圍起來,請看下面的例子。

還不懂shell指令碼核心?這一篇就夠了!

實戰詳解:

使用 expr 進行數學計算是多麼的麻煩呀,需要注意各種細節,工作中不推薦使用。

7。2 使用方括號[ ]

bash shell為了保持跟Bourne shell的相容而包含了expr命令,但它同樣也提供了一種更簡單的方法來執行數學表示式。在bash中,在將一個數學運算結果賦給某個變數時,可以用美元符和 方括號($[ operation ])將數學表示式圍起來。

還不懂shell指令碼核心?這一篇就夠了!

用方括號執行shell數學運算比用expr命令方便很多。這種技術也適用於shell指令碼。

還不懂shell指令碼核心?這一篇就夠了!

需要額外注意的是bash shell數學運算子只支援整數運算。若要進行任何實際的數學計算,這是一個巨大的限制。如下圖:

還不懂shell指令碼核心?這一篇就夠了!

八、退出指令碼

迄今為止所有的示例指令碼中,我們都是突然停下來的。執行完最後一條命令時,指令碼就結束

了。其實還有另外一種更優雅的方法可以為指令碼劃上一個句號。

shell中執行的每個命令都使用退出狀態碼(exit status)告訴shell它已經執行完畢。退出狀態

碼是一個0~255的整數值,在命令結束執行時由命令傳給shell。可以捕獲這個值並在指令碼中使用。

8。1 檢視退出狀態碼

Linux提供了一個專門的變數$?來儲存上個已執行命令的退出狀態碼。對於需要進行檢查的命令,必須在其執行完畢後立刻檢視或使用$?變數。它的值會變成由shell所執行的最後一條命令 的退出狀態碼。

還不懂shell指令碼核心?這一篇就夠了!

如果命令成功結束,那麼它退出的狀態碼就是 0,如果是失敗的,那狀態碼就是一個非零的正數值。

還不懂shell指令碼核心?這一篇就夠了!

無效命令會返回一個退出狀態碼127。Linux錯誤退出狀態碼沒有什麼標準可循,但有一些可

用的參考,如下圖:

還不懂shell指令碼核心?這一篇就夠了!

退出狀態碼126表明使用者沒有執行命令的正確許可權。

還不懂shell指令碼核心?這一篇就夠了!

另一個會碰到的常見錯誤是給某個命令提供了無效引數。

還不懂shell指令碼核心?這一篇就夠了!

8。2 exit

exit 是一個 Shell 內建命令,用來退出當前 Shell 程序,並返回一個退出狀態;使用

$?

可以接收這個退出狀態。

exit 命令可以接受一個整數值作為引數,代表退出狀態。如果不指定,預設狀態值是 0。

一般情況下,退出狀態為 0 表示成功,退出狀態為非 0 表示執行失敗(出錯)了。

exit 退出狀態只能是一個介於 0~255 之間的整數,其中只有 0 表示成功,其它值都表示失敗。

Shell 程序執行出錯時,可以根據退出狀態來判斷具體出現了什麼錯誤,比如開啟一個檔案時,我們可以指定 1 表示檔案不存在,2 表示檔案沒有讀取許可權,3 表示檔案型別不對。

還不懂shell指令碼核心?這一篇就夠了!

可以看到,

“after exit”

並沒有輸出,這說明遇到 exit 命令後,test1執行就結束了。

實戰詳解:

注意,exit 表示退出當前 Shell 程序,我們必須在新程序中執行 test1,否則當前 Shell 會話(終端視窗)會被關閉,我們就無法看到輸出結果了。

標簽: shell  命令  指令碼  輸出  變數