跳至內容

Bash

本頁使用了標題或全文手工轉換
維基百科,自由的百科全書
Bash
Bash會話的截圖
Bash會話的截圖
原作者布萊恩·福克斯
首次釋出1989年6月8日,​35年前​(1989-06-08
目前版本
  • 5.2.37(2024年9月23日;穩定版本)[1]
編輯維基數據連結
原始碼庫 編輯維基數據連結
程式語言C語言
作業系統
平台GNU
語言多語言(gettext
類型Unix shell命令語言英語command language
特許條款GPLv3+[6]
網站www.gnu.org/software/bash/

BashUnix shell的一種,在1987年由布萊恩·福克斯為了GNU計劃而編寫。1989年釋出第一個正式版本,原先是計劃用在GNU作業系統上,但能執行於大多數類Unix系統的作業系統之上,包括LinuxMac OS X v10.4起至macOS Mojave都將它作為預設shell,而自macOS Catalina,預設Shell以zsh取代。

Bash是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的beta版本,版本號為.99。在福克斯離開於1992年中期到1994年中期的某個時候離開自由軟件基金會之前,他一直擔任Bash的主要維護者。之後,他的工作被傳遞給另一個早期貢獻者,切特·雷米(Chet Ramey)。

從那時起,在Linux用戶當中sh在很大度上成為最流行的shell,並成為許多Linux發行版預設的互動式shell(不過Almquist shell可能是預設的指令碼shell)。在蘋果公司的 OS X 作業系統上也是如此。Bash 也被移植到 Microsoft Windows(通過CygwinMinGW)。通過DJGPP專案,Bash被移植到了DOS。通過許多終端模擬軟件,Bash被移植到Novell NetWareAndroid。微軟在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和PNG檔案。

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 回車字元
\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背景執行,則單獨在結尾處使用&,例子如下:

command1 &

要並行執行兩個命令,它們必須用&分隔,例子如下:

command1 & command2

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

通過 Control+Z 可以將當前行程掛起(放置後台並暫停執行),可通過 fg 命令恢復至前台,也通過bg將掛起的行程背景執行。

檢視行程狀態

[編輯]

所有行程的情況,我們可以通過jobs命令檢視,包含正在背景執行和停止了的。

$ jobs
[1]-  Running                  command1 &
[2]+  Stopped                  command2

上例的輸出結果中,中括號包圍的數字(如: "[1]" 和 "[2]" )為job的ID號。加號(+)用於指定fgbg命令的預設對象job。減號( - )用於指定,若當前的預設對象job退出後,下一個預設對象job是誰。"Running" 和 "Stopped" 表示行程狀態。 最後一個欄位為命令。[7]


總結:

  • 一般命令在前台執行(fg),執行完畢後,控制返回給用戶。
  • 在命令後面加上&,它會在後台執行(bg),並將特殊的環境變數$!設置為該任務的行程ID。這時shell可以並行執行其他命令。
  • 後台程式試圖寫入數據到終端裝置時(與寫入標準輸出不同)可能被阻塞。
  • 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. ^ Index of /gnu/bash. [2024年12月4日]. 
  2. ^ Bash FAQ, version 4.14. [2016-04-09]. (原始內容存檔於2018-09-01). 
  3. ^ Why does Apple ship bash 3.2?. apple.stackexchange.com. [2019-04-25]. (原始內容存檔於2016-04-16). 
  4. ^ Missing source code - GPL compliance? · Issue #107 · Microsoft/WSL. GitHub. [2019-04-25]. (原始內容存檔於2019-09-24). 
  5. ^ GNU Bash. Softpedia. SoftNews. [2016-04-09]. (原始內容存檔於2017-10-21). 
  6. ^ GNU Project. README file. [2019-04-25]. (原始內容存檔於2019-04-26). 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). 
  7. ^ jobs.1p - Linux manual page. man7.org. [2020-01-14]. (原始內容存檔於2020-01-14). 

外部連結

[編輯]
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy