Bash Shell 指令碼/簡單命令
一個簡單命令由以空格或製表符分隔的詞語序列組成。第一個詞語被認為是命令的名稱,其餘詞語作為引數傳遞給命令。我們已經看到了許多簡單命令的示例;這裡有一些更多示例
cd ..- 此命令使用
cd(“更改目錄”;一個用於在檔案系統中導航的內建命令)向上導航一個目錄。 - 符號
..表示“父目錄”。例如,/foo/bar/../baz.txt等價於/foo/baz.txt。
- 此命令使用
rm foo.txt bar.txt baz.txt- 假設程式
rm(“刪除”)已安裝,此命令將刪除當前目錄中的檔案foo.txt、bar.txt和baz.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 -
| 提示 使用沒有引數的 |
要列印你當前所在的路徑,可以使用另一個名為pwd 的內建 Unix 實用程式。
我們在上面看到命令rm foo.txt bar.txt baz.txt 刪除了三個單獨的檔案:foo.txt、bar.txt 和 baz.txt。這是因為 Bash 根據空格將命令拆分成四個獨立的詞語,其中三個詞語成為程式rm 的引數。但如果我們需要刪除一個名稱中包含空格的檔案怎麼辦?
| 注意 在 Unix、GNU/Linux 發行版和其他類似 Unix 的系統中,檔名可以包含空格、製表符、換行符,甚至控制字元。 |
Bash 提供了多種引用機制,這些機制在這種情況下非常有用;最常用的機制是單引號' 和雙引號"。以下兩個命令都會刪除一個名為this file.txt 的檔案
rm 'this file.txt'
rm "this file.txt"
在引號內,空格字元將失去其作為詞語分隔符的特殊含義。通常我們將整個詞語放在引號中,如上所示,但實際上,實際上只需要將空格本身括起來即可;this' 'file.txt 或 this" "file.txt 等價於'this file.txt'。
另一種常用的引用機制是反斜槓\,但它的工作方式略有不同;它只引用(或“轉義”)一個字元。因此,此命令等價於上面的命令
rm this\ file.txt
在所有這些情況下,引用字元本身都不會傳遞給程式。(這稱為引用移除。)因此,rm 無法知道它是以何種方式呼叫的——例如——以rm foo.txt 還是rm 'foo.txt' 的形式呼叫的。
Bash 支援多種特殊符號,稱為擴充套件,用於將常用的引數型別傳遞給程式。
其中之一是檔名擴充套件,其中模式(如*.txt)將被替換為與該模式匹配的所有檔案的名稱。例如,如果當前目錄包含檔案foo.txt、bar.txt、this file.txt 和 something.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。
| 注意 如果任何檔名以連字元 |
如果我們有一個名為*.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
第一個命令顯式地列出每個引數。另外三個命令都使用花括號擴充套件來更簡潔地表達引數:在第二個命令中,給出所有可能性1 到 5,以逗號分隔;在第三個命令中,給出數字序列(“從 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 且不包含 bar 的 input.txt 行。