跳轉到內容

Bash Shell 指令碼/簡單命令

來自華夏公益教科書,開放的書籍,開放的世界

一個簡單命令由以空格或製表符分隔的詞語序列組成。第一個詞語被認為是命令的名稱,其餘詞語作為引數傳遞給命令。我們已經看到了許多簡單命令的示例;這裡有一些更多示例

  • cd ..
    • 此命令使用cd(“更改目錄”;一個用於在檔案系統中導航的內建命令)向上導航一個目錄。
    • 符號..表示“父目錄”。例如,/foo/bar/../baz.txt 等價於 /foo/baz.txt
  • rm foo.txt bar.txt baz.txt
    • 假設程式rm(“刪除”)已安裝,此命令將刪除當前目錄中的檔案foo.txtbar.txtbaz.txt
    • Bash 透過搜尋可配置的目錄列表來查詢程式 rm,尋找名為 rm 且可執行的檔案(由其檔案許可權決定)。
  • /foo/bar/baz bip.txt
    • 此命令執行位於/foo/bar/baz 的程式,並將 bip.txt 作為唯一引數傳遞。
    • /foo/bar/baz 必須可執行(由其檔案許可權決定)。
    • 警告:請確保正斜槓和後面任何檔案之間沒有任何空格。 例如,假設“foo”資料夾存在於“根”目錄中,那麼執行以下命令:“rm -r / foo” 如果在“sudo”訪問許可權下執行,將會摧毀你的電腦。你已被警告。如果你不理解前面提到的內容,現在不必擔心。
    • 如果/foo/bar/baz 是一個文字檔案而不是二進位制程式,並且它的第一行以#!開頭,那麼該行剩下的部分將決定用於執行該檔案的直譯器。例如,如果/foo/bar/baz 的第一行是#!/bin/bash,那麼上面的命令等價於/bin/bash /foo/bar/baz bip.txt

使用/foo/bar/baz 的那個示例特別值得注意,因為它說明了如何建立一個像普通程式一樣執行的 Bash 指令碼:只需將#!/bin/bash 作為指令碼的第一行(假設 Bash 位於你係統的那個位置;如果不是,請根據需要調整),並確保指令碼具有可讀和可執行的正確檔案許可權。在這本書的剩餘部分,所有完整 shell 指令碼示例都將以#!/bin/bash 行開頭。

正如我們在第一個示例中看到的,實用程式命令cd 用於使用給定引數進行導航。在 Unix 和類似 Unix 的系統(如 GNU/Linux)中,存在兩種型別的路徑,分別稱為相對路徑和絕對路徑。相對路徑相對於你的當前位置,而絕對路徑是特定的。以下是一個導航到絕對路徑的示例

cd 'path/to/directory'

對於相對路徑導航,有一些特定的引數可以實現不同的快捷方式。以下是一些這些引數

要向上導航兩個目錄,你需要輸入

cd ../../

要導航到你之前導航到當前位置的路徑,你需要輸入

cd -
提示

使用沒有引數的cd 直接導航到系統上的預設主目錄。

要列印你當前所在的路徑,可以使用另一個名為pwd 的內建 Unix 實用程式。

我們在上面看到命令rm foo.txt bar.txt baz.txt 刪除了三個單獨的檔案:foo.txtbar.txtbaz.txt。這是因為 Bash 根據空格將命令拆分成四個獨立的詞語,其中三個詞語成為程式rm 的引數。但如果我們需要刪除一個名稱中包含空格的檔案怎麼辦?

注意

在 Unix、GNU/Linux 發行版和其他類似 Unix 的系統中,檔名可以包含空格、製表符、換行符,甚至控制字元。

Bash 提供了多種引用機制,這些機制在這種情況下非常有用;最常用的機制是單引號' 和雙引號"。以下兩個命令都會刪除一個名為this file.txt 的檔案

rm 'this file.txt'
rm "this file.txt"

在引號內,空格字元將失去其作為詞語分隔符的特殊含義。通常我們將整個詞語放在引號中,如上所示,但實際上,實際上只需要將空格本身括起來即可;this' 'file.txtthis" "file.txt 等價於'this file.txt'

另一種常用的引用機制是反斜槓\,但它的工作方式略有不同;它只引用(或“轉義”)一個字元。因此,此命令等價於上面的命令

rm this\ file.txt

在所有這些情況下,引用字元本身都不會傳遞給程式。(這稱為引用移除。)因此,rm 無法知道它是以何種方式呼叫的——例如——以rm foo.txt 還是rm 'foo.txt' 的形式呼叫的。

檔名擴充套件和波浪號擴充套件

[編輯 | 編輯原始碼]

Bash 支援多種特殊符號,稱為擴充套件,用於將常用的引數型別傳遞給程式。

其中之一是檔名擴充套件,其中模式(如*.txt)將被替換為與該模式匹配的所有檔案的名稱。例如,如果當前目錄包含檔案foo.txtbar.txtthis file.txtsomething.else,那麼此命令

echo *.txt

等價於此命令

echo 'bar.txt' 'foo.txt' 'this file.txt'

這裡的星號* 表示“零個或多個字元”;還有其他一些特殊的模式字元(如問號?,表示“正好一個字元”),以及一些其他模式匹配規則,但這種對* 的使用是迄今為止模式的最常見用法。

檔名擴充套件不一定侷限於當前目錄中的檔案。例如,如果我們想列出/usr/bin 目錄中所有與t*.sh 匹配的檔案,我們可以這樣寫

echo /usr/bin/t*.sh

這可能擴充套件成類似於這樣

echo /usr/bin/test.sh /usr/bin/time.sh

如果沒有任何檔案與指定的模式匹配,則不會進行替換;例如,此命令

echo asfasefasef*avzxv

很可能只會列印asfasefasef*avzxv

注意

如果任何檔名以連字元- 開頭,那麼檔名擴充套件有時會產生意想不到的結果。例如,如果一個目錄包含兩個檔案,名為-ntmp.txt,那麼cat * 將擴充套件為cat -n tmp.txtcat 將把-n 解釋為選項而不是檔名;相反,最好寫cat ./*cat -- *,這將擴充套件為cat ./-n ./tmp.txtcat -- -n tmp.txt,從而消除此問題。

如果我們有一個名為*.txt 的實際檔案,我們想引用它怎麼辦?(是的,檔名可以包含星號!)那麼我們可以使用上面看到的任何一種引用方式,或者使用反斜槓來消除星號的特殊含義。以下兩種方式

cat '*.txt'
cat "*.txt"
cat \*.txt

將列印實際檔案*.txt,而不是列印所有名稱以.txt 結尾的檔案。

另一個類似的擴充套件是波浪號擴充套件。波浪號擴充套件有很多功能,但主要功能是:在僅包含波浪號~ 的詞語中,或者在以~/(波浪號-斜槓)開頭的詞語中,波浪號將被替換為當前使用者主目錄的完整路徑。例如,此命令

echo ~/*.txt

將列印當前使用者主目錄中所有名為*.txt 的檔案的名稱。

花括號擴充套件

[編輯 | 編輯原始碼]

與檔名擴充套件類似,花括號擴充套件是一種簡潔的方式來表示多個相似的引數。以下四個命令是等價的

ls file1.txt file2.txt file3.txt file4.txt file5.txt
ls file{1,2,3,4,5}.txt
ls file{1..5..1}.txt
ls file{1..5}.txt

第一個命令顯式地列出每個引數。另外三個命令都使用花括號擴充套件來更簡潔地表達引數:在第二個命令中,給出所有可能性15,以逗號分隔;在第三個命令中,給出數字序列(“從 1 到 5,步長為 1”);第四個命令與第三個相同,但將..1 隱式省略。

我們也可以以相反的順序列出檔案

ls file5.txt file4.txt file3.txt file2.txt file1.txt
ls file{5,4,3,2,1}.txt
ls file{5..1..-1}.txt
ls file{5..1}.txt

當序列的終點小於起點時,預設步長為-1

由於在 Bash 中,命令的第一個詞語是執行的程式,所以我們也可以這樣寫命令

{ls,file{1..5}.txt}

但顯然這不利於可讀性。(順便說一下,檔名擴充套件也可以做到類似的事情。)

大括號擴充套件,類似於檔名擴充套件,可以透過任何引號機制來停用;'{'"{"\{ 會生成一個實際的字面大括號。

重定向輸出

[編輯 | 編輯原始碼]

Bash 允許將命令的標準輸出(檔案描述符 1)傳送到檔案,而不是傳送到控制檯。例如,常見的實用程式 cat 將檔案寫入標準輸出;如果我們將它的標準輸出重定向到檔案,那麼我們就可以將一個檔案的內容複製到另一個檔案。

如果我們想用命令的輸出覆蓋目標檔案,我們使用這種表示法

cat input.txt > output.txt

如果我們想保留目標檔案的現有內容,只將命令的輸出追加到末尾,我們使用這種表示法

cat input.txt >> output.txt

然而,並非程式寫入控制檯的所有內容都透過標準輸出。許多程式使用標準錯誤(檔案描述符 2)來顯示錯誤訊息和某些型別的“日誌記錄”或“側通道”訊息。如果我們希望標準錯誤與標準輸出合併,我們可以使用以下兩種表示法中的任何一種

cat input.txt &>> output.txt
cat input.txt >> output.txt 2>&1

如果我們希望將標準錯誤追加到與標準輸出不同的檔案,我們使用這種表示法

cat input.txt >> output.txt 2>> error.txt

事實上,我們可以只重定向標準錯誤,而保留標準輸出

cat input.txt 2>> error.txt

在以上所有示例中,如果我們想覆蓋重定向目標而不是追加到它,我們可以用 > 替換 >>

稍後我們將看到一些更高階的關於輸出重定向的操作。

重定向輸入

[編輯 | 編輯原始碼]

就像 Bash 允許將程式的輸出傳送到檔案一樣,它也允許從檔案獲取程式的輸入。例如,常見的 Unix 實用程式 cat 將其輸入複製到其輸出,這樣這個命令

cat < input.txt

會將 input.txt 的內容寫入控制檯。

正如我們已經看到的,在這種情況下,不需要這個技巧,因為 cat 可以被指示將指定的檔案複製到它的輸出;上面的命令只是等效於下面的命令

cat input.txt

這是規則而不是例外;大多數常見的 Unix 實用程式,它們可以從控制檯獲取輸入,也具有從檔案獲取輸入的內建功能。事實上,許多程式(包括 cat)可以從多個檔案獲取輸入,使它們比上面更靈活。以下命令將打印出 input1.txt,然後是 input2.txt

cat input1.txt input2.txt

儘管如此,輸入重定向有其用途,我們將在後面看到其中一些用途。

管道預覽

[編輯 | 編輯原始碼]

雖然它們超出了本章的範圍,但我們現在已經有了對管道進行初步瞭解所需的背景知識。管道是一系列由管道字元 | 分隔的命令。每個命令同時執行,每個命令的輸出用作下一個命令的輸入。

例如,考慮以下管道

cat input.txt | grep foo | grep -v bar

我們已經看到了 cat 實用程式;cat input.txt 只是將檔案 input.txt 寫入其標準輸出。程式 grep 是一個常見的 Unix 實用程式,它根據模式進行過濾(在 Unix 行話中稱為“grep”);例如,命令 grep foo 會將包含字串 foo 的所有輸入行列印到其標準輸出。命令 grep -v bar 使用 -v 選項反轉模式;該命令列印所有包含字串 bar 的輸入行。由於每個命令的輸入都是前一個命令的輸出,因此最終結果是該管道列印所有包含 foo包含 barinput.txt 行。

華夏公益教科書