本頁使用了標題或全文手工轉換

bash

維基百科,自由的百科全書
前往: 導覽搜尋
Bash
Bash demo.png
展示部分特性的Bash與sh會話截圖sessions demonstrating some features
原作者 布賴恩·福克斯
初始版本 1989年6月7日,​27年前​(1989-06-07
穩定版本 4.3(2014年2月27日,​2年前​(2014-02-27[±][1]
開發狀態 Active
程式語言 C
作業系統 跨平台
系統平台 GNU
語言 英語,多語言(gettext
類型 Unix shell
授權條款 GNU通用公眾授權條款第3版+[2]
網站 www.gnu.org/software/bash/

bashUnix shell的一種,在1987年由布萊恩·福克斯為了GNU計劃而編寫。1989年釋出第一個正式版本,原先是計劃用在GNU作業系統上,但能執行於大多數類Unix系統的作業系統之上,包括LinuxMac OS X v10.4都將它作為預設shell。它也被移植到Microsoft Windows上的CygwinMinGW,或是可以在MS-DOS上使用的DJGPP專案。在Novell NetWareAndroid在上也有移植。1990年後,Chet Ramey成為了主要的維護者。

Bourne shell的後繼相容版本與開放原始碼版本,它的名稱來自Bourne shell(sh)的一個雙關語(Bourne again / born again):Bourne-Again SHell。

Bash是一個命令處理器,通常執行於文字窗口中,並能執行用戶直接輸入的命令。Bash還能從檔案中讀取命令,這樣的檔案稱為指令碼。和其他Unix shell 一樣,它支援檔案名取代(通配符符合)、管道here文件、命令取代、變數,以及條件判斷和迴圈遍歷的結構控制語句。包括關鍵字、語法在內的基本特性全部是從sh借鑑過來的。其他特性,例如歷史命令,是從cshksh借鑑而來。總的來說,Bash雖然是一個滿足POSIX規範的shell,但有很多擴充功能。

一個名為Shellshock的安全漏洞在2014年9月初被發現,並迅速導致網際網路上的一系列攻擊。這個漏洞可追溯到1989年釋出的1.03版本。

歷史[編輯]

由於理察·斯托曼對於之前一位開發者的進度不滿,布萊恩·福克斯從1988年1月10日開始開發Bash。斯托曼和自由軟體基金會希望到一個能夠執行已有的shell指令碼的自由軟體看。他們把這作是建成一個基於BSD和GNU的完全自由的作業系統的戰略的重要部分。這是他們自己注資的幾個專案之一。福克斯作為自由軟體基金會的雇員承擔了這項工作。1989年6月8日,福克斯釋出了Bash的測公測版本號為.99。在福克斯離開於1992年中期到1994年中期的某個時候離開自由軟體基金會之前,他一直擔任Bash的主要維護者。之後,他的工作被傳遞給另一個早期貢獻者,切特·雷米(Chet Ramey)。

從那時起,在Linux用戶當中sh在很大度上成為了最流行的shell,並成為許多Linux發行版預設的互動式shell(不過 Almquist shell 可能是預設的指令碼shell)。在蘋果公司的 OS X 作業系統上也是如此。Bash 也被移植到 Microsoft Windows(通過Cygwin和MinGW)。通過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(ksh)和C shell(csh),例如命令列編輯,命令歷史,目錄棧,$RANDOM$PPID變數,以及POSIX的命令置換語法:$(...)。作為一個互動式的shell,按下TAB鍵即可自動補全已部分輸入的程式名,檔案名,變數名等等。

使用'function'關鍵字時,Bash的函式聲明與Bourne/Korn/POSIX指令碼不相容(Korn shell 有同樣的問題)。不過Bash也接受Bourne/Korn/POSIX的函式聲明語法。因為許多不同,Bash指令碼很少能在Bourne或Korn直譯器中執行,除非編寫指令碼時刻意保持相容性。然而,隨著Linux的普及,這種方式正變得越來越少。不過在POSIX模式下,Bash更加符合POSIX。

bash的語法針對Bourne shell的不足做了很多擴充功能。其中的一些列舉在這裡。

花括弧擴充功能[編輯]

花括弧擴充功能是一個從C shell借鑑而來的特性,它產生一系列指定的字串(按照原先從左到右的順序)。這些字串不需要是已經存在的檔案。

$ echo a{p,c,d,b}e
ape ace ade abe
$ echo {a,b,c}{d,e,f}
ad ae af bd be bf cd ce cf

花括弧擴充功能不應該被用在可移植的shell指令碼中,因為Bourne shell產生的結果不同。

#! /bin/sh

# 传统的shell并不产生相同结果
echo a{p,c,d,b}e # a{p,c,d,b}e

當花括弧擴充功能和通配符一起使用時,花括弧擴充功能首先被解析,然後正常解析通配符。因此,可以用這種方法獲得當前目錄的一系列JPEG和PEG檔案。

ls *.{jpg,jpeg,png}    # 首先扩展为*.jpg *.jpeg *.png,然后解析通配符
echo *.{png,jp{e,}g}   # echo显示扩展结果;花括号扩展可以嵌套。

除了列舉備選項,還可以用「..」在花括弧擴充功能中指定字元或數字範圍。較新的Bash版本接受一個整數作為第三個參數,指定增量。

$ echo {1..10}
1 2 3 4 5 6 7 8 9 10
$ echo file{1..4}.txt
file1.txt file2.txt file3.txt file4.txt
$ echo {a..e}
a b c d e
$ echo {1..10..3}
1 4 7 10
$ echo {a..j..3}
a d g j

當花括弧擴充功能和變數擴充功能一起使用時,變數擴充功能解析於花括弧擴充功能之後。有時有必要使用內建的eval函式

$ start=1; end=10
$ echo {$start..$end}  # 由于解析顺序,无法得到想要的结果
{1..10}
$ 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(大多數情況下是1),可以用於是與非的條件判斷:

 if((VAR == Y * 3 + X * 2))
 then
         echo Yes
 fi

 ((Z > 23)) && echo Yes

((...))命令支援下列比較操作符:'==', '!=', '>', '<', '>=',和'<='。

bash不能在自身行程內進行浮點數運算。當前有這個能力的unix shell只有Korn shellZ shell

輸入輸出重新導向[編輯]

bash擁有傳統Bourne shell缺乏的I/O重新導向語法。bash可以同時重新導向標準輸出和標準錯誤,這需要使用下面的語法:

 command &> file

這比等價的Bourne shell語法"command > file 2>&1"來的簡單。2.05b版本以後,bash可以用下列語法重新導向標準輸入至字串(稱為here string):

 command <<< "string to be read as standard input"

如果字串包括空格就需要用引號包裹字串。

例子: 重新導向標準輸出至檔案,寫資料,關閉檔案,重設標準輸出。

 # 生成标准输出(文件描述符1)的拷贝文件描述符6
 exec 6>&1
 # 打开文件"test.data"以供写入
 exec 1>test.data
 # 产生一些内容
 echo "data:data:data"
 # 关闭文件"test.data"
 exec 1>&-
 # 使标准输出指向FD 6(重置标准输出)
 exec 1>&6
 # 关闭FD6
 exec 6>&-

開啟及關閉檔案

 # 打开文件test.data以供读取
 exec 6<test.data
 # 读文件直到文件尾
 while read -u 6 dta
 do
   echo "$dta"
 done
 # 关闭文件test.data
 exec 6<&-

抓取外部命令的輸出

  # 运行'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(.*)' ]]
 then
         echo The regex matches!
         echo $BASH_REMATCH      -- outputs: foobarbletch
         echo ${BASH_REMATCH[1]} -- outputs: bar
         echo ${BASH_REMATCH[2]} -- outputs: etch
 fi

使用這個語法的效能要比生成一個新的行程來執行grep命令優越,因為正規表示式符合在bash行程內完成。如果正規表示式或者字串包括空格或者shell 關鍵字,(諸如'*'或者'?'),就需要用引號包裹。Bash 4 開始的版本已經不需要這麼做了。

轉義字元[編輯]

$'string' 形式的字串會被特殊處理。字串會被展開成string,並像C語言那樣將反斜槓及緊跟的字元進行取代。反斜槓轉義序列的轉換方式如下:

轉義字元
轉義字元 擴充功能成...
\a 響鈴符
\b 退格符
\e ANSI轉義符,等價於\033
\f 饋頁符
\n 換行符
\r Enter符
\t 水平制表符
\v 垂直制表符
\\ 反斜槓
\' 單引號
\nnn 十進位值為nnn的8-bit字元(1-3位)
\xHH 十六進位值為HH的8-bit字元(1或2位)
\cx control-X字元

擴充功能後的結果將被單引號包裹,就好像美元符號一直就不存在一樣。

雙引號包裹的字串前若有一個美元符號($"...")將會使得字串被翻譯成符合當前locale的語言。如果當前locale是C或者POSIX,美元符號會被忽略。如果字串被翻譯並取代,取代後的字串仍被雙引號包裹。

關聯陣列[編輯]

Bash 4.0 開始支援關聯陣列,通過類似AWK的方式,對於多維陣列提供了偽支援。

$ declare -A a         # 声明一个名为a的伪二位数组
$ i=1; j=2
$ a[$i,$j]=5           # 将键 "$i,$j" 与值 5 对应
$ echo ${a[$i,$j]}

移植性[編輯]

呼叫Bash時指定 --posix 或者在指令碼中聲明 set -o posix ,可以使得Bash幾乎遵循 POSIX 1003.2 標準。若要保證一個Bash指令碼的移植性,至少需要考慮到 Bourne shell,即Bash取代的shell。Bash有一些傳統的 Bourne shell 所沒有的特性,包括以下這些:

  • 某些擴充功能的呼叫選項
  • 命令取代(即$())(儘管這是 POSIX 1003.2 標準的一部分)
  • 花括弧擴充功能
  • 某些陣列操作、關聯陣列
  • 擴充功能的雙層方括弧判斷語句
  • 某些字串生成操作
  • 行程取代
  • 正規表示式符合符
  • Bash特有的內建工具
  • 協行程

鍵盤快速鍵[編輯]

Bash預設使用Emacs的快速鍵,可以通過 set -o vi 讓它使用Vi的快速鍵

行程管理[編輯]

Bash有兩種執行命令的模式:批次處理模式、併發模式。 要以批次處理模式執行命令(即按照順序),必須用;分隔

command1 ; command2

在這個例子中,當command1執行完畢,即執行command2 要並行執行兩個命令,它們必須用&分隔

command1 & command2

在這種情況下,command1 在後台執行(通過&),從而立即將控制返回到shell,以執行command2

總結:

  • 一般命令在前台執行(fg),執行完畢後,控制返回給用戶。
  • 在命令後面加上&,它會在後台執行(bg),並將特殊的環境變數$!設定為該任務的行程ID。這時shell可以並行執行其他命令。
  • Ctrl+z可以掛起前台執行的程式
  • 掛起的程式可以用fg取消復原到前台,或者用bg取消復原到後台
  • 後台程式試圖寫入資料到終端裝置時(與寫入標準輸出不同)可能被阻塞。
  • shell可以等待一個後台任務執行完成,只需使用wait命令,加上行程ID或者任務序號;也可以等待所有的後台任務,只需使用不加參數的wait

啟動指令碼[編輯]

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參數)它使用POSIX標準來讀取啟動檔案。在此模式下,互動shells擴充功能變數ENV,從以此為檔案名的檔案中讀取命令並執行。

bash會探測自己是不是被遠端shell守護程式執行(通常是rshd)。如果是,它會讀取並執行~/.bashrc中的命令。但是rshd一般不會用rc相關參數呼叫shell,也不會允許指定這些參數。

Bash與Bourne shell和csh啟動指令碼的比較[編輯]

Bash的特性是從Bourne shell和csh發展而來,因此一定程度上允許同Bourne shell的啟動檔案分享,並提供一些csh用戶熟悉的啟動特性。

設定可繼承的環境變數[編輯]

Bourne shell登陸時使用 ~/.profile 來設定環境變數,這些環境變數可以被子行程繼承。Bash可以以相容的方式使用~/.profile ,只需在Bash自有的指令碼中顯式執行下面這行代碼。通過在~/.profile 中避免使用Bash特有的語法,就可以和Bourne shell保持相容性。

. ~/.profile

別名和函式[編輯]

更通用的函式以及借鑑自csh的「別名(alias)」很大程度上取代了Bourne shell的別名(alias)和函式。然而這兩個特性一般不能從登入式shell中繼承,在該登入式shell的子shell中,它們必須被重新定義。儘管有個環境變數ENV可以被用於這個問題,不過 csh 和 Bash 都可以用子shell的啟動指令碼直接處理。在Bash當中,~/.bashrc 是互動式子shell啟動時執行的指令碼。如果想要在登入式shell中使用 ~/.bashrc 定義的函式,可以在 ~/.bash_login 的環境變數後面加上這樣一行:

. ~/.bashrc

登入與登出時執行的命令[編輯]

最初登入時,csh 執行 ~/.login ,可以執行一些只在登入時執行的操作,例如顯示系統負載、硬碟狀態、是否收到新郵件、在記錄檔中記錄登入時間,等等。Bourne shell 可以在 ~/.profile 檔案中模擬這種行為,但並沒有預先定義檔案名。可以在 ~/.bash_profile 檔案的環境變數設定和函式定義的後面添加這樣一行:

. ~/.bash_login

與之類似,csh還有一個檔案 ~/.logout ,這個檔案只在登入式shell退出時執行。Bash與之對應的檔案是 ~/.bash_logout ,並且不需要專門的設定。在 Bourne shell 中,trap 這個內建工具可以實作類似的效果。

相容舊環境的Bash啟動指令碼範例[編輯]

下面這個 ~/.bash_profile 的框架與 Bourne shell 相容,並且為 ~/.bashrc~/.bash_login 提供類似於 csh 的語意。[ -r 文件名] 測試指定檔案是否存在,如果不存在,跳過 && 後面的部分

[ -r ~/.profile ] && . ~/.profile             # 只使用Bourne shell的语法设置环境变量
if [ -n "$PS1" ] ; then                       # 判断是否是交互式shell
   [ -r ~/.bashrc     ] && . ~/.bashrc        # 加载~/.bashrc(tty、prompt、函数设置等)
   [ -r ~/.bash_login ] && . ~/.bash_login    # 执行登录式shell登录时的任务
fi                                            # if块的结束标志

Bash 啟動指令碼與作業系統相關的問題[編輯]

一些 Unix 和 Linux 版本常在 /etc 放置 Bash 的系統級啟動指令碼。Bash在其標準的初始化過程中執行它們,不過其他啟動指令碼可以按照不同於Bash啟動序列文件所述的順序來讀取這些檔案。root 用戶的檔案預設內容,以及新用戶被建立時系統提供的預設檔案可能有問題。啟動 X Window系統 的啟動指令碼可能使用用戶的 Bash 啟動指令碼嘗試在 視窗管理員 啟動之前設定用戶的環境變數。這些問題常常可以通過使用 ~/.xsession 或者 ~/.xprofile 來讀取 ~/.profile 而解決。

註腳[編輯]

  1. ^ Ramey, Chet. Bash-4.3 available for FTP. info-gnu mailing list. 2014-02-27 [2014-02-27]. 
  2. ^ GNU Project. README file. Bash is free software, distributed under the terms of the [GNU] General Public License as published by the Free Software Foundation, version 3 of the License (or any later version). 

外部連結[編輯]