命令列萬用字元教程

阮一峰發表於2018-09-20

一次性操作多個檔案時,命令列提供萬用字元(wildcards),用一種很短的文字模式(通常只有一個字元),簡潔地代表一組路徑。

萬用字元又叫做 globbing patterns。因為 Unix 早期有一個/etc/glob檔案儲存萬用字元模板,後來 Bash 內建了這個功能,但是這個名字被保留了下來。

萬用字元早於正規表示式出現,可以看作是原始的正規表示式。它的功能沒有正則那麼強大靈活,但是勝在簡單和方便。

本文介紹 Bash 的各種萬用字元。

一、? 字元

?字元代表單個字元。


# 存在檔案 a.txt 和 b.txt
$ ls ?.txt
a.txt b.txt

上面命令中,?表示單個字元,所以會同時匹配a.txtb.txt

如果匹配多個字元,就需要多個?連用。


# 存在檔案 a.txt、b.txt 和 ab.txt
$ ls ??.txt
ab.txt

上面命令中,??匹配了兩個字元。

注意,?不能匹配空字元。也就是說,它佔據的位置必須有字元存在。

二、* 字元

*代表任意數量的字元。


# 存在檔案 a.txt、b.txt 和 ab.txt
$ ls *.txt
a.txt b.txt ab.txt

# 輸出所有檔案
$ ls *

上面程式碼中,*匹配任意長度的字元。

*可以匹配空字元。


# 存在檔案 a.txt、b.txt 和 ab.txt
$ ls a*.txt
a.txt ab.txt

三、[...] 模式

[...]匹配方括號之中的任意一個字元,比如[aeiou]可以匹配五個母音字母。


# 存在檔案 a.txt 和 b.txt
$ ls [ab].txt
a.txt b.txt

$ ls *[ab].txt
ab.txt a.txt b.txt

[start-end]表示一個連續的範圍。


# 存在檔案 a.txt、b.txt 和 c.txt
$ ls [a-c].txt
a.txt b.txt c.txt

# 存在檔案 report1.txt、report2.txt 和 report3.txt
$ ls report[0-9].txt
report1.txt report2.txt report3.txt

四、[^...][!...]

[^...][!...]表示匹配不在方括號裡面的字元(不包括空字元)。這兩種寫法是等價的。


# 存在檔案 a.txt、b.txt 和 c.txt
$ ls [^a].txt
b.txt c.txt

這種模式下也可以使用連續範圍的寫法[!start-end]


$ echo report[!1-3].txt
report4.txt report5.txt

上面程式碼中,[!1-3]表示排除1、2和3。

五、{...} 模式

{...} 表示匹配大括號裡面的所有模式,模式之間使用逗號分隔。


$ echo d{a,e,i,u,o}g
dag deg dig dug dog

它可以用於多字元的模式。


$ echo {cat,dog}
cat dog

{...}[...]有一個很重要的區別。如果匹配的檔案不存在,[...]會失去模式的功能,變成一個單純的字串,而{...}依然可以展開。


# 不存在 a.txt 和 b.txt
$ ls [ab].txt
ls: [ab].txt: No such file or directory

$ ls {a,b}.txt
ls: a.txt: No such file or directory
ls: b.txt: No such file or directory

上面程式碼中,如果不存在a.txtb.txt,那麼[ab].txt就會變成一個普通的檔名,而{a,b}.txt可以照樣展開。

大括號可以巢狀。


$ echo {j{p,pe}g,png}
jpg jpeg png

大括號也可以與其他模式聯用。


$ echo {cat,d*}
cat dawg dg dig dog doug dug

上面程式碼中,會先進行大括號擴充套件,然後進行*擴充套件。

六、{start..end} 模式

{start..end}會匹配連續範圍的字元。


$ echo d{a..d}g
dag dbg dcg ddg

$ echo {11..15}
11 12 13 14 15

如果遇到無法解釋的擴充套件,模式會原樣輸出。


$ echo {a1..3c}
{a1..3c}

這種模式與逗號聯用,可以寫出複雜的模式。


$ echo .{mp{3..4},m4{a,b,p,v}}
.mp3 .mp4 .m4a .m4b .m4p .m4v

七、注意點

萬用字元有一些使用注意點,不可不知。

(1)萬用字元是先解釋,再執行。

Bash 接收到命令以後,發現裡面有萬用字元,會進行萬用字元擴充套件,然後再執行命令。


$ ls a*.txt
ab.txt

上面命令的執行過程是,Bash 先將a*.txt擴充套件成ab.txt,然後再執行ls ab.txt

(2)萬用字元不匹配,會原樣輸出。

Bash 擴充套件萬用字元的時候,發現不存在匹配的檔案,會將萬用字元原樣輸出。


# 不存在 r 開頭的檔名
$ echo r*
r*

上面程式碼中,由於不存在r開頭的檔名,r*會原樣輸出。

下面是另一個例子。


$ ls *.csv
ls: *.csv: No such file or directory

另外,前面已經說過,這條規則對{...}不適用

(3)只適用於單層路徑。

上面所有萬用字元只匹配單層路徑,不能跨目錄匹配,即無法匹配子目錄裡面的檔案。或者說,?*這樣的萬用字元,不能匹配路徑分隔符(/)。

如果要匹配子目錄裡面的檔案,可以寫成下面這樣。


$ ls */*.txt

(4)可用於檔名。

Bash 允許檔名使用萬用字元。這時,引用檔名的時候,需要把檔名放在單引號裡面。


$ touch 'fo*'
$ ls
fo*

上面程式碼建立了一個fo*檔案,這時*就是檔名的一部分。

八、參考連結

(完)

相關文章