Paul Brown 解釋了 Linux shell 命令中那個不起眼的“點”的各種意思和用法。
在現實情況中,使用 shell 命令編寫的單行命令或指令碼可能會令人很困惑。你使用的很多工具的名稱與它們的實際功能相差甚遠(grep
、tee
和 awk
,還有嗎?),而當你將兩個或更多個組合起來時,所組成的 “句子” 看起來更像某種外星人的天書。
因此,上面說的這些對於你並無幫助,因為你用來編寫一連串的指令所使用的符號根據你使用的場景有著不同的意義。
就拿這個不起眼的點(.
)來說吧。當它放在一個需要一個目錄名稱的命令的引數處時,表示“當前目錄”:
find . -name "*.jpg"
意思就是“在當前目錄(包括子目錄)中尋找以 .jpg
結尾的檔案”。
ls .
和 cd .
結果也如你想的那樣,它們分別列舉和“進入”到當前目錄,雖然在這兩種情況下這個點都是多餘的。
而一個緊接著另一個的兩個點呢,在同樣的場景下(即當你的命令期望一個檔案目錄的時候)表示“當前目錄的父目錄”。如果你當前在 /home/your_directory
下並且執行:
cd ..
你就會進入到 /home
。所以,你可能認為這仍然適合“點代表附近目錄”的敘述,並且毫不複雜,對吧?
那下面這樣會怎樣呢?如果你在一個檔案或目錄的開頭加上點,它表示這個檔案或目錄會被隱藏:
$ touch somedir/file01.txt somedir/file02.txt somedir/.secretfile.txt$ ls -l somedir/total 0-rw-r--r-- 1 paul paul 0 Jan 13 19:57 file01.txt-rw-r--r-- 1 paul paul 0 Jan 13 19:57 file02.txt$ # 注意上面列舉的檔案中沒有 .secretfile.txt$ ls -la somedir/total 8drwxr-xr-x 2 paul paul 4096 Jan 13 19:57 .drwx------ 48 paul paul 4096 Jan 13 19:57 ..-rw-r--r-- 1 paul paul 0 Jan 13 19:57 file01.txt-rw-r--r-- 1 paul paul 0 Jan 13 19:57 file02.txt-rw-r--r-- 1 paul paul 0 Jan 13 19:57 .secretfile.txt$ # 這個 -a 選項告訴 ls 去展示“all”檔案,包括那些隱藏的
然後就是你可以將 .
當作命令。是的,你聽我說:.
是個真真正正的命令。它是 source
命令的代名詞,所以你可以用它在當前 shell 中執行一個檔案,而不是以某種其它的方式去執行一個指令碼檔案(這通常指的是 Bash 會產生一個新的 shell 去執行它)
很困惑?別擔心 —— 試試這個:建立一個名為 myscript
的指令碼,內容包含下面一行:
myvar="Hello"
然後通過常規的方法執行它,也就是用 sh myscript
(或者通過 chmod a+x myscript
命令讓它可執行,然後執行 ./myscript
)。現在嘗試並且觀察 myvar
的內容,通過 echo $myvar
(理所當然你什麼也得不到)。那是因為,當你的指令碼賦值 "Hello"
給 myvar
時,它是在一個隔離的bash shell 範例中進行的。當指令碼執行結束時,這個新產生的範例會消失並且將控制權轉交給原來的shell,而原來的 shell 裡甚至都不存在 myvar
變數。
然而,如果你這樣執行 myscript
:
. myscript
echo $myvar
就會列印 Hello
到命令列上。
當你的 .bashrc
檔案發生變化後,你經常會用到 .
(或 source
)命令,就像當你要擴充套件 PATH
變數那樣。在你的當前 shell 範例中,你可以使用 .
來讓變化立即生效。
就像看似無關緊要的一個點有多個含義一樣,兩個點也是如此。除了指向當前目錄的父級之外,兩個點(..
)也用於構建序列。
嘗試下這個:
echo {1..10}
它會列印出從 1 到 10 的序列。在這種場景下,..
表示 “從左邊的值開始,計數到右邊的值”。
現在試下這個:
echo {1..10..2}
你會得到 1 3 5 7 9
。..2
這部分命令告訴 Bash 輸出這個序列,不過不是每個相差 1,而是相差 2。換句話說,就是你會得到從 1 到 10 之間的奇數。
它反著也仍然有效:
echo {10..1..2}
你也可以用多個 0 填充你的數位。這樣:
echo {000..121..2}
會這樣列印出從 0 到 121 之間的偶數(填充了前置 0):
000 002 004 006 ... 050 052 054 ... 116 118 120
不過這樣的序列發生器有啥用呢?當然,假設您的新年決心之一是更加謹慎控制您的帳戶花銷。作為決心的一部分,您需要建立目錄,以便對過去 10 年的數位發票進行分類:
mkdir {2009..2019}_Invoices
工作完成。
或者你可能有數百個帶編號的檔案,比如從視訊剪輯中提取的幀,或許因為某種原因,你只想從第 43 幀到第 61 幀每隔三幀刪除一幀:
rm frame_{043..61..3}
很可能,如果你有超過 100 個幀,它們將以填充 0 命名,如下所示:
frame_000 frame_001 frame_002 ...
那就是為什麼你在命令中要用 043
,而不是43
的原因。
說實話,序列的神奇之處不在於雙點,而是花括號({}
)的巫術。看看它對於字母是如何工作的。這樣做:
touch file_{a..z}.txt
它建立了從 file_a.txt
到 file_z.txt
的檔案。
但是,你必須小心。使用像 {Z..a}
這樣的序列將產生一大堆大寫字母和小寫字母之間的非字母、數位的字元(既不是數位或字母的字形)。其中一些字形是不可列印的或具有自己的特殊含義。使用它們來生成檔名稱可能會導致一系列意外和可能令人不快的影響。
最後一件值得指出的事:包圍在 {...}
的序列,它們也可以包含字串列表:
touch {blahg,splurg,mmmf}_file.txt
將建立了 blahg_file.txt
、splurg_file.txt
和 mmmf_file.txt
。
當然,在別的場景中,大括號也有不同的含義(驚喜嗎!)。不過那是別的文章的內容了。
Bash 以及執行於其中的各種工具已經被尋求解決各種特定問題的系統管理員們把玩了數十年。要說這種有自己之道的系統管理員是一種特殊物種的話,那是有點輕描淡寫。總而言之,與其他語言相反,Bash 的設計目標並不是為了使用者友好、簡單、甚至合乎邏輯。
但這並不意味著它不強大 —— 恰恰相反。Bash 的語法和 shell 工具可能不一致且很龐大,但它們也提供了一系列令人眼花繚亂的方法來完成您可能想象到的一切。就像有一個工具箱,你可以從中找到從電鑽到勺子的所有東西,以及橡皮鴨、一捲膠帶和一些指甲鉗。
除了引人入勝之外,探明你可以直接在 shell 中達成的所有能力也很有趣,所以下次我們將深入探討如何構建更大更好的 Bash 命令列。
在那之前,玩得開心!