Bash

Bash，Unix shell的一種，在1987年由布萊恩·福克斯為了GNU計劃而編寫。1989年釋出第一個正式版本，原先是計劃用在GNU作業系統上，但能運行於大多數類Unix系統的作業系統之上，包括Linux與Mac OS X v10.4起至macOS Mojave都將它作為預設shell，而自macOS Catalina，預設Shell以zsh取代。Bash是Bourne shell的後繼相容版本與開放原始碼版本，它的名稱來自Bourne shell：Bourne-Again SHell。Bash是一個命令處理器，通常運行於文字視窗中，並能執行使用者直接輸入的命令。Bash還能從檔案中讀取命令，這樣的檔案稱為指令碼。和其他Unix shell 一樣，它支援檔名替換、管道、here文件、命令替換、變數，以及條件判斷和迴圈遍歷的結構控制語句。包括關鍵字、語法在內的基本特性全部是從sh借鑑過來的。其他特性，例如歷史命令，是從csh和ksh借鑑而來。總的來說，Bash雖然是一個滿足POSIX規範的shell，但有很多擴充套件。一個名為Shellshock的安全漏洞在2014年9月初被發現，並迅速導致網際網路上的一系列攻擊。這個漏洞可追溯到1989年釋出的1.03版本。歷史.由於理查德·斯托曼對於之前一位開發者的進度不滿，布萊恩·福克斯從1988年1月10日開始開發Bash。斯托曼和自由軟體基金會希望到一個能夠執行已有的shell指令碼的自由軟體。他們把這看作是建成一個基於BSD和GNU的完全自由的作業系統的戰略的重要部分。這是他們自己注資的幾個專案之一。福克斯作為自由軟體基金會的僱員承擔這項工作。1989年6月8日，福克斯釋出Bash的beta版本，版本號為.99。在福克斯離開於1992年中期到1994年中期的某個時候離開自由軟體基金會之前，他一直擔任Bash的主要維護者。之後，他的工作被傳遞給另一個早期貢獻者，切特·雷米。從那時起，在Linux使用者當中sh在很大度上成為最流行的shell，併成為許多Linux發行版預設的互動式shell。透過DJGPP專案，Bash被移植到了DOS。透過許多終端模擬軟體，Bash被移植到Novell NetWare和Android。微軟在2016年的Build大會上宣佈，Windows 10 新增一個Linux子系統，完全支援Bash和其他Ubuntu下的二進位制程式。2014年9月24日，Stephane Chazelas，一位工作於英國，致力於Unix/Linux和網路通訊方面的專家，發現Bash的一個安全漏洞。這個漏洞被命名為Shellshock，並被分配編號 CVE-2014-6271、CVE-2014-6277、CVE-2014-7169。這個漏洞非常嚴重，因為使用Bash的CGI指令碼會變得脆弱，使得攻擊者可以執行任意的程式碼。這個漏洞與Bash透過環境變數把函式定義傳遞給shell子程序的方式有關。語法與特性.bash的命令語法是Bourne shell命令語法的超集。數量龐大的Bourne shell指令碼大多不經修改即可以在bash中執行，只有那些引用了Bourne特殊變數或使用了Bourne的內建命令的指令碼才需要修改。bash的命令語法很多來自Korn shell，例如命令列編輯，命令歷史，目錄棧，$RANDOM和$PPID變數，以及POSIX的命令置換語法：$(...)。作為一個互動式的shell，按下TAB鍵即可自動補全已部分輸入的程式名，檔名，變數名等等。使用'function'關鍵字時，Bash的函式宣告與Bourne/Korn/POSIX指令碼不相容。不過Bash也接受Bourne/Korn/POSIX的函式宣告語法。因為許多不同，Bash指令碼很少能在Bourne或Korn直譯器中執行，除非編寫指令碼時刻意保持相容性。然而，隨著Linux的普及，這種方式正變得越來越少。不過在POSIX模式下，Bash更加符合POSIX。bash的語法針對Bourne shell的不足做了很多擴充套件。其中的一些列舉在這裡。花括號擴充套件.花括號擴充套件是一個從C shell借鑑而來的特性，它產生一系列指定的字串。這些字串不需要是已經存在的檔案。$ echo a{p,c,d,b}eape ace ade abead ae af bd be bf cd ce cf花括號擴充套件不應該被用在可移植的shell指令碼中，因為Bourne shell產生的結果不同。echo a{p,c,d,b}e # a{p,c,d,b}e當花括號擴充套件和萬用字元一起使用時，花括號擴充套件首先被解析，然後正常解析萬用字元。因此，可以用這種方法獲得當前目錄的一系列JPEG和PNG檔案。ls *.{jpg,jpeg,png} # 首先擴充套件為*.jpg *.jpeg *.png，然後解析萬用字元echo *.{png,jp{e,}g} # echo顯示擴充套件結果；花括號擴充套件可以巢狀。除了列舉備選項，還可以用“..”在花括號擴充套件中指定字元或數字範圍。較新的Bash版本接受一個整數作為第三個引數，指定增量。1 2 3 4 5 6 7 8 9 10$ echo file{1..4}.txtfile1.txt file2.txt file3.txt file4.txta b c d e1 4 7 10a d g j當花括號擴充套件和變數擴充套件一起使用時，變數擴充套件解析於花括號擴充套件之後。有時有必要使用內建的codice_1函式$ start=1; end=10$ echo {$start..$end} # 由於解析順序，無法得到想要的結果$ eval echo {$start..$end} # 首先進行變數擴充套件的解析1 2 3 4 5 6 7 8 9 10使用整數.與Bourne shell不同的是bash不用另外生成程序即能進行整數運算。bash使用((...))命令和$[...]變數語法來達到這個目的：VAR=55 # 將整數55賦值給變數VAR((VAR = VAR + 1)) # 變數VAR加1。注意這裡沒有'$'((++VAR)) # 另一種方法給VAR加1。使用C語言風格的字首自增((VAR++)) # 另一種方法給VAR加1。使用C語言風格的字尾自增echo $((VAR * 22)) # VAR乘以22並將結果送入命令echo $[VAR * 22] # 同上，但為過時用法((...))命令可以用於條件語句，因為它的退出狀態是0或者非0，可以用於是與非的條件判斷：if((VAR == Y * 3 + X * 2))thenecho Yesfi((Z &gt; 23)) &amp;&amp; echo Yes((...))命令支援下列比較運算子：'==', '!=', '&gt;', '='，和'&amp;-開啟及關閉檔案# 開啟檔案test.data以供讀取exec 6&lt;test.data# 讀檔案直到檔案尾while read -u 6 dtadoecho "$dta"done# 關閉檔案test.dataexec 6&lt;&amp;-抓取外部命令的輸出# 執行'find'並且將結果存於VAR# 搜尋以"h"結尾的檔名VAR=$(find . -name "*h")程序內的正則表示式.bash 3.0支援程序內的正則表示式，使用下面的語法：string =~ regex正則表示式語法同regex(7) man page所描述的一致。正則表示式匹配字串時上述命令的退出狀態為0，不匹配為1。正則表示式中用圓括號括起的子表示式可以訪問shell變數BASH_REMATCH，如下：if abcfoobarbletch =~ 'foo(bar)bl(.*)'thenecho The regex matches!echo $BASH_REMATCH -- outputs: foobarbletchecho ${BASH_REMATCH[1]} -- outputs: barecho ${BASH_REMATCH[2]} -- outputs: etchfi使用這個語法的效能要比生成一個新的程序來執行grep命令優越，因為正則表示式匹配在bash程序內完成。如果正則表示式或者字串包括空格或者shell 關鍵字，，就需要用引號包裹。Bash 4 開始的版本已經不需要這麼做了。跳脫字元.$'string' 形式的字串會被特殊處理。字串會被展開成string，並像C語言那樣將反斜槓及緊跟的字元進行替換。反斜槓轉義序列的轉換方式如下：擴充套件後的結果將被單引號包裹，就好像美元符號一直就不存在一樣。雙引號包裹的字串前若有一個美元符號將會使得字串被翻譯成符合當前locale的語言。如果當前locale是C或者POSIX，美元符號會被忽略。如果字串被翻譯並替換，替換後的字串仍被雙引號包裹。關聯陣列.Bash 4.0 開始支援關聯陣列，透過類似AWK的方式，對於多維陣列提供了偽支援。$ declare -A a # 宣告一個名為a的偽二位陣列$ i=1; j=2$ a[$i,$j]=5 # 將鍵 "$i,$j" 與值 5 對應移植性.呼叫Bash時指定 codice_2 或者在指令碼中宣告 codice_3 ，可以使得Bash幾乎遵循 POSIX 1003.2 標準。若要保證一個Bash指令碼的移植性，至少需要考慮到 Bourne shell，即Bash取代的shell。Bash有一些傳統的 Bourne shell 所沒有的特性，包括以下這些：鍵盤快捷鍵.Bash預設使用Emacs的快捷鍵，可以透過 codice_5 讓它使用Vi的快捷鍵程序管理.Bash有兩種執行命令的模式：批處理模式、併發模式。批處理模式.要以 批處理模式 執行命令，必須用codice_6分隔，例子如下：command1 ; command2在這個例子中，當codice_7執行完畢，即執行codice_8。併發模式.使codice_7後臺執行，則單獨在結尾處使用codice_10，例子如下：command1 &amp;要併發執行兩個命令，它們必須用codice_10分隔，例子如下：command1 &amp; command2在這種情況下，codice_7 在後臺執行，從而立即將控制返回到shell，以執行codice_8。透過 可以將當前程序掛起，可透過 codice_15 命令恢復至前臺，也透過codice_16將掛起的程序後臺執行。檢視程序狀態.所有程序的情況，我們可以透過codice_17命令檢視，包含正在後臺執行和停止了的。$ jobs[1]- Running command1 &amp;[2]+ Stopped command2上例的輸出結果中，中括號包圍的數字為job的ID號。加號(+)用於指定codice_15和codice_16命令的預設物件job。減號( - )用於指定，若當前的預設物件job退出後，下一個預設物件job是誰。"Running" 和 "Stopped" 表示程序狀態。 最後一個欄位為命令。總結：啟動指令碼.bash啟動的時候會執行各種不同的指令碼。當bash作為一個登入的互動shell被呼叫，或者作為非互動shell但帶有--login引數被呼叫時，它首先讀入並執行檔案/etc/profile。然後它會依次尋找~/.bash_profile，~/.bash_login，和~/.profile，讀入並執行第一個存在且可讀的檔案。--noprofile引數可以阻止bash啟動時的這種行為。當一個登入shell退出時，bash讀取並執行~/.bash_logout檔案，如果此檔案存在。當一個互動的非登入shell啟動後，bash讀取並執行~/.bashrc檔案。這個行為可以用--norc引數阻止。--rcfile file引數強制bash讀取並執行指定的file而不是預設的~/.bashrc。如果用sh來呼叫bash，bash在啟動後進入"posix"模式，它會盡可能模仿sh歷史版本的啟動行為，以便遵守POSIX標準。用sh名字呼叫的非互動shell不會去讀取其他啟動指令碼，--rcfile引數無效。當bash以POSIX模式啟動時它使用POSIX標準來讀取啟動檔案。在此模式下，互動shells擴充套件變數ENV，從以此為檔名的檔案中讀取命令並執行。bash會探測自己是不是被遠端shell守護程式執行。如果是，它會讀取並執行~/.bashrc中的命令。但是rshd一般不會用rc相關引數呼叫shell，也不會允許指定這些引數。Bash與Bourne shell和csh啟動指令碼的比較.Bash的特性是從Bourne shell和csh發展而來，因此一定程度上允許同Bourne shell的啟動檔案共享，並提供一些csh使用者熟悉的啟動特性。設定可繼承的環境變數.Bourne shell登陸時使用 codice_26 來設定環境變數，這些環境變數可以被子程序繼承。Bash可以以相容的方式使用codice_26 ，只需在Bash自有的指令碼中顯式執行下面這行程式碼。透過在codice_26 中避免使用Bash特有的語法，就可以和Bourne shell保持相容性。. ~/.profile別名和函式.更通用的函式以及借鑑自csh的“別名(alias)”很大程度上取代了Bourne shell的別名(alias)和函式。然而這兩個特性一般不能從登入式shell中繼承，在該登入式shell的子shell中，它們必須被重新定義。儘管有個環境變數codice_29可以被用於這個問題，不過 csh 和 Bash 都可以用子shell的啟動指令碼直接處理。在Bash當中，codice_30 是互動式子shell啟動時執行的指令碼。如果想要在登入式shell中使用 codice_30 定義的函式，可以在 codice_32 的環境變數後面加上這樣一行：. ~/.bashrc登入與登出時執行的命令.最初登入時，csh 執行 codice_33 ，可以執行一些只在登入時執行的操作，例如顯示系統負載、硬碟狀態、是否收到新郵件、在日誌中記錄登入時間，等等。Bourne shell 可以在 codice_26 檔案中模擬這種行為，但並沒有預先定義檔名。可以在 codice_35 檔案的環境變數設定和函式定義的後面新增這樣一行：. ~/.bash_login與之類似，csh還有一個檔案 codice_36 ，這個檔案只在登入式shell退出時執行。Bash與之對應的檔案是 codice_37 ，並且不需要專門的設定。在 Bourne shell 中，codice_38 這個內建工具可以實現類似的效果。相容舊環境的Bash啟動指令碼示例.下面這個 codice_35 的框架與 Bourne shell 相容，並且為 codice_30 和 codice_32 提供類似於 csh 的語義。codice_42 測試指定檔案是否存在，如果不存在，跳過 codice_43 後面的部分[ -r ~/.profile ] &amp;&amp; . ~/.profile # 只使用Bourne shell的語法設定環境變數if [ -n "$PS1" ] ; then # 判斷是否是互動式shell[ -r ~/.bashrc ] &amp;&amp; . ~/.bashrc # 載入~/.bashrc[ -r ~/.bash_login ] &amp;&amp; . ~/.bash_login # 執行登入式shell登入時的任務fi # if塊的結束標誌Bash 啟動指令碼與作業系統相關的問題.一些 Unix 和 Linux 版本常在 codice_44 放置 Bash 的系統級啟動指令碼。Bash在其標準的初始化過程中執行它們，不過其他啟動指令碼可以按照不同於Bash啟動序列文件所述的順序來讀取這些檔案。root 使用者的檔案預設內容，以及新使用者被建立時系統提供的預設檔案可能有問題。啟動 X Window系統 的啟動指令碼可能使用使用者的 Bash 啟動指令碼嘗試在 視窗管理器 啟動之前設定使用者的環境變數。這些問題常常可以透過使用 codice_45 或者 codice_46 來讀取 codice_26 而解決。