跳轉到內容

Bourne Shell 指令碼/執行命令

來自華夏公益教科書,自由的教科書,為自由的世界

在我們開始對 Bourne Shell 的能力進行任何形式的考察以及如何利用其能力之前,我們必須先了解一些基本內容:我們必須討論如何將命令輸入 shell 以供該 shell 執行。

簡單的方法:互動式會話

[編輯 | 編輯原始碼]

再看一看你可能已經見過的內容

[編輯 | 編輯原始碼]

如果你可以訪問基於 Unix 的機器(或其他作業系統上的模擬器),你可能已經在使用 Bourne Shell - 或它的後代 - 了,可能沒有意識到。驚喜:你已經做了一段時間的 shell 指令碼了!

在你的 Unix 環境中,進入一個終端;無論是文字登入終端,還是如果你正在使用 X Window System 的視窗中的終端(如果你還沒有這樣做,可以查詢名為 *xterm* 或 *rxvt* 或 *terminal* 的東西)。你最終可能會看到一個類似這樣的螢幕

Ben_Tels:Local_Machine:~>_

或者

The admin says: everybody, STOP TRYING TO CRASH THE SYSTEM
Have a lot of fun!
bzt:Another_Machine:~>_

甚至像這樣簡單的東西

$_

就是這樣。這就是你的 shell:你直接訪問系統提供的所有內容。

在互動模式下使用 shell

[編輯 | 編輯原始碼]

具體來說,你剛才訪問的程式是你的 shell,它在 *互動模式* 下執行:shell 以這樣一種方式執行,它會顯示一個提示符和一個游標(那個閃爍的小線),並在等待你輸入一個命令供它執行。你在互動模式下執行命令的方式是輸入命令,然後按下 **Enter** 鍵。shell 然後將你的命令轉換為作業系統能夠理解的內容,並將控制權交給作業系統,以便它能夠實際執行你傳送的任務。你會注意到,當命令正在執行時,你的游標會短暫地消失,並且你不能再輸入任何內容(此時,Bourne Shell 程式不再控制你的終端 - 你透過執行命令啟動的另一個程式正在控制)。在某個時刻,作業系統將完成你的命令的工作,shell 將顯示一個新的提示符和游標,然後開始再次等待你輸入另一個命令。試試看:輸入命令

ls enter

過一小段時間後,你會看到工作目錄(你的 shell 認為是“當前”目錄)中的檔案列表,一個新的提示符和游標。

這是執行 shell 命令最簡單的方法:一次輸入一個命令,並等待每個命令按順序完成。shell 經常以這種方式使用,既用於執行屬於 Bourne Shell 程式語言的命令,也用於簡單地啟動其他程式(例如上面的示例中的 ls 程式)。

一個有用的點滴

[編輯 | 編輯原始碼]

在我們繼續之前,我們提一下在使用 shell 時兩個有用的按鍵組合:用於中斷正在執行的程式和 shell 命令的命令,以及用於退出 shell 的命令(儘管,為什麼你永遠想要 *停止* 使用 shell,我無法理解……)。

要中斷正在執行的程式或 shell 命令,請同時按下 Control 和 C 鍵。我們將在後面的章節中詳細瞭解這到底做了什麼,但現在請記住,這是中斷操作的方式。

要退出 shell 會話,請按下 Control+d。此按鍵組合會產生 Unix 檔案結束符 - 我們將在後面討論為什麼這也會終止你的 shell 會話。一些現代 shell 停用了 Control+d 的使用,轉而使用“exit”命令(可恥)。如果你使用的是這種 shell,只需輸入“exit”一詞(就像其他任何命令一樣),然後按下 Enter(從現在開始,我會在示例中省略“Enter”)。

稍微不那麼簡單的方法:指令碼

[編輯 | 編輯原始碼]

正如我們在上一節中看到的,你可以透過啟動一個互動式 shell 會話並在提示符下輸入命令,非常容易地執行所有目的的 shell 命令。但是,有時你有一組命令需要定期重複,即使是在不同的時間和不同的 shell 會話中。當然,在 Unix 系統的以程式設計為中心的環境中,你可以編寫一個程式來獲得相同的結果(例如在 C 語言中)。但是,使用 shell 來完成這項任務是否會方便得多?是否會有更方便的方法來重放一組命令?以及能夠像在 shell 的互動式會話中輸入單個命令一樣輕鬆地編寫該組命令?

shell 指令碼

[編輯 | 編輯原始碼]

幸運的是,確實有這樣的方法:Bourne Shell 的 *非互動* 模式。在這種模式下,shell 沒有提示符,也不會等待你的命令。相反,shell 從一個文字檔案(它告訴 shell 要做什麼,有點像演員從劇本中獲取命令 - 因此,shell 指令碼)中讀取命令。該檔案包含一系列命令,就像你在互動式會話中的提示符下輸入它們一樣。shell 從上到下讀取檔案,並按順序執行命令。

shell 指令碼非常容易編寫;你可以使用任何你喜歡的文字編輯器(甚至可以使用任何文字處理器或其他編輯器,只要記住將你的指令碼儲存為純文字格式即可)。你編寫命令的方式與你在互動式 shell 中編寫命令的方式相同。你可以在儲存指令碼後立即執行指令碼;不需要編譯它或任何其他操作。

執行 shell 指令碼

[編輯 | 編輯原始碼]

要執行 shell 指令碼(讓 shell 讀取它並執行指令碼中的所有命令),您可以在互動式 shell 提示符下輸入命令,就像您執行其他操作時一樣(如果您使用的是圖形使用者介面,您也可以透過單擊滑鼠來執行指令碼)。在這種情況下,您要啟動的程式本身就是 shell 程式。例如,要執行名為 MyScript 的指令碼,您可以在互動式 shell 中輸入以下命令(假設指令碼位於您的工作目錄中)

執行指令碼
sh MyScript


從 shell 程式內部啟動 shell 程式乍聽起來可能很奇怪,但如果您仔細想想就會明白。畢竟,您在互動模式的 shell 會話中鍵入命令。要執行指令碼,您需要在非互動模式下啟動一個 shell。這就是上面命令中發生的事情。您會注意到,在上面的示例中,Bourne Shell 可執行檔案接收一個引數:要執行的指令碼的名稱。

如果您碰巧使用的是符合 POSIX 1003.1 標準的 shell,您也可以在這個新的非互動式會話中執行單個命令。您必須使用 -c 命令列開關來告訴 shell 您正在傳遞一個命令,而不是指令碼的名稱。

在新 shell 中執行命令
sh -c ls


我們將在稍後進一步瞭解為什麼要這樣做(而不是簡單地直接將您的命令輸入互動式 shell)。

還有一種從互動式 shell 執行指令碼的方法:鍵入執行命令(一個點)後跟指令碼的名稱。

原始碼指令碼
. MyScript


它與使用 sh 命令的區別在於,sh 命令啟動一個新的程序,而執行命令不會啟動新程序。我們將在下一節中討論這個問題(及其重要性)。順便說一下,這種帶點的表示法通常被稱為原始碼一個指令碼。

以另一種方式執行 shell 指令碼

[編輯 | 編輯原始碼]

還有一種方法可以執行 shell 指令碼,即更直接地使用 Unix 作業系統的一個功能:可執行模式。

在 Unix 中,每個檔案都有三個不同的許可權(讀、寫和執行),可以針對三個不同的實體設定:擁有該檔案的使用者、該檔案所屬的組以及“世界”(所有其他人)。輸入以下命令

ls -l


在互動式 shell 中檢視工作目錄中所有檔案的許可權(包含最多九個字母的列,r、w 和 x 代表讀、寫和執行,前三個代表使用者,中間三個代表組,後面的三個代表世界)。只要其中一個實體具有“執行”許可權,該實體就可以簡單地將該檔案作為程式執行。要使您的指令碼可供所有人執行,請使用以下命令

chmod +x 指令碼名稱


就像這樣

使 MyScript 可執行
chmod +x MyScript


然後,您可以使用簡單的命令執行該指令碼(假設它位於您的 PATH 中的目錄中,即 shell 在您沒有告訴它確切的位置時搜尋程式的目錄)

在新 shell 中執行命令
MyScript


如果失敗,那麼當前目錄可能不在您的 PATH 中。您可以使用以下方法強制執行該指令碼

使 shell 在當前目錄中查詢您的指令碼
./MyScript


在該命令下,作業系統會檢查該檔案,將其放入記憶體並允許它像任何其他程式一樣執行。當然,並非所有檔案都有意義作為程式;二進位制檔案不一定是計算機可以識別的一組命令,而文字檔案根本無法被計算機讀取。因此,要使我們的指令碼像這樣執行,我們必須做一些額外的操作。

正如我們之前提到的,Unix 作業系統首先會檢查程式。如果程式是文字檔案而不是二進位制檔案(不能簡單地執行),作業系統會期望檔案的第一行命名直譯器,作業系統應該啟動該直譯器來解釋檔案的其餘部分。Unix 作業系統期望找到的行如下所示

#!直譯器的完整路徑和名稱


在我們的例子中,以下行幾乎可以在任何地方使用

#!/bin/sh


Bourne Shell 可執行檔案,位於 bin 目錄中,該目錄位於檔案系統樹的頂層下方。例如

帶有顯式直譯器的 Bourne shell 指令碼

程式碼:

#!/bin/sh
echo Hello World!

輸出:

Hello World!

以這種方式執行 shell 指令碼有幾個優點。首先,它比其他表示法更簡潔(需要更少的輸入)。其次,如果您要將指令碼傳遞給他人,這是一種額外的安全措施。您不必依賴他們擁有正確的 shell,而只需要指定他們應該使用哪個 shell。如果 Bourne Shell 足夠,您就可以要求使用它。如果您絕對需要 kshbash,您可以指定使用它們(注意,這並非萬無一失——其他人可以透過使用我們上面討論過的其他命令之一來執行您的指令碼,即使指令碼可能無法正常執行,即使他們這樣做了)。

順便說一句,Unix 並不將這種技巧限制在 shell 指令碼上。任何期望其指令碼為純文字的指令碼直譯器都可以用這種方式指定。您可以使用相同的技巧來建立直接可執行的 Perl 指令碼或 Python、Ruby 等指令碼,以及 Bourne Shell 指令碼。

還要注意,在使用 bash 作為預設 shell 的發行版中,您可以使用 #!/bin/sh shebang 並使用典型的 bash 語法在您的指令碼中。它可以工作。但是,為了使相同的指令碼在不使用 bash 作為預設 shell 的發行版(例如 Debian)中也能正常工作,您必須修改該指令碼或將它的 shebang 更改為 #!/bin/bash。

關於 Unix 和多處理的一點

[編輯 | 編輯原始碼]

為什麼您需要了解多處理

[編輯 | 編輯原始碼]

雖然這不是一本關於 Unix 的書,但我們必須瞭解 Unix 作業系統的一些方面,才能完全理解 Bourne Shell 偶爾為何以這種方式工作。

Unix 作業系統最重要的方面之一——實際上,它是將它與所有其他主流作業系統區分開來的主要方面——是 Unix 作業系統一直都是一個多使用者、多處理作業系統(與其他作業系統相比,例如 MacOS 和 Microsoft 的 DOS/Windows 作業系統)。Unix OS 一直旨在執行由多個使用者同時使用的機器,這些使用者都希望同時執行至少一個,甚至可能是多個程式。作業系統將機器處理器的執行時間分配給多個程式的能力,使其對使用者來說似乎都在同時執行,被稱為多處理。Unix 作業系統從核心開始就被設計為具有這種可能性,它會影響 shell 會話的行為方式。

每當您在 Unix 機器上啟動一個新程序(例如,透過執行一個程式)時,作業系統都會為該程序提供它自己的操作環境。該環境包括一些供程序使用的記憶體,還可以包括所有程序的某些預定義設定。每當您執行 shell 程式時,它都在自己的環境中執行。

每當您從另一個程序啟動一個新程序時(例如,在互動模式下向您的 shell 程式發出命令),新程序就會成為第一個程序的子程序(例如,ls 程式作為您的 shell 的子程序執行)。這就是了解多處理和程序互動變得重要的原因:子程序總是以副本的形式開始父程序的環境。這意味著兩件事

  1. 子程序永遠無法更改其父程序的操作環境——它只能訪問該環境的副本;
  2. 如果您確實想要更改 shell 的環境(或者具體而言,想要避免更改它),您必須瞭解何時命令作為子程序執行,何時命令在當前 shell 中執行;否則,您可能會選擇一個與您想要的結果相反的效果。

哪個做什麼

[編輯 | 編輯原始碼]

我們已經看到了幾種執行 shell 命令或指令碼的方法。關於多處理,它們以以下方式執行

執行方式 執行為
互動模式命令
  • shell 命令的當前環境 [1])
  • 新程式的子程序
Shell 非互動模式 子程序
點符號執行命令 (. MyScript) 當前環境
透過 Unix 可執行許可權和直譯器選擇 子程序

一個有用的知識:後臺程序

[編輯 | 編輯原始碼]

透過以上內容,您可能會認為多程序在 shell 指令碼中很麻煩。但是,如果真是這樣,我們就不會有“多程序”了——Unix 不會保留無用之物。多程序是與系統其餘部分互動的寶貴工具,可以用來提高工作效率。關於多程序在程式開發中的優勢有很多書籍,但從 Bourne Shell 使用者和指令碼編寫者的角度來看,主要優勢是可以將程序的控制權交給作業系統, *並在子程序執行時繼續工作*。實現此目的的方法是將您的程序作為 *後臺程序* 執行。

將程序作為後臺程序執行意味著告訴作業系統您想啟動一個程序,但它不應附加到其父程序正在使用的任何互動式裝置(鍵盤、螢幕等)。更重要的是,它還告訴作業系統應立即返回啟動子程序的請求,並且應允許父程序繼續工作,而不必等待其子程序結束。

這聽起來很複雜,但您必須記住,這種能力完全融入到 Unix 作業系統中,Bourne Shell 旨在作為 Unix 強大功能的簡單介面。換句話說:Bourne Shell 包括以其自身簡單命令的形式啟動子程序的能力。讓我們透過一個例子來演示如何做到這一點以及這種能力有多有用。在提示符下輸入以下(有點無用但仍很耗時)命令:

N=0 && while [ $N -lt 10000 ]; do date >> scriptout; N=`expr $N + 1`; done

我們將在後面的章節中介紹它的含義;現在,您只需知道此命令向系統請求日期和時間,並將結果寫入名為“scriptout”的檔案。由於它隨後重複此過程 10000 次,因此可能需要一些時間才能完成。

現在輸入以下命令:

N=0 && while [ $N -lt 10000 ]; do date >> scriptout; N=`expr $N + 1`; done&

您會注意到,您可以立即恢復使用 shell(如果您沒有看到這種情況,請按 Ctrl+C 並檢查您在末尾是否添加了額外的 ampersand)。一段時間後,後臺程序將完成,scriptout 檔案將包含另外 10000 次時間讀取。

在 Bourne Shell 中啟動後臺程序的方法是在命令後面附加一個 ampersand (&)。


^ 實際上,您也可以在這裡強制執行子程序 - 我們將在討論命令分組時瞭解如何執行。


下一頁: 環境 | 上一頁: 比較 Shell
主頁: Bourne Shell 指令碼
華夏公益教科書