Bash 萬用字元的歷史
Bash“萬用字元”和正則表達式不相同是否有歷史原因?例如,我相信在 Bash
[1-2]*
中匹配以 1 或 2 開頭的任何內容,然後是其他任何內容,而作為正則表達式[1-2]*
將僅匹配 1 和 2 的序列。我的 Bash 腳本和 REGEX foo 都很弱,我經常遇到與這些差異相關的問題,這讓我很好奇它們為什麼不同。
bash
最初在 80 年代後期設計為ksh
具有 csh/tcsh 的一些互動功能的部分複製。globbing 的起源必須在它所建立的那些早期的 shell 中找到。
ksh
它本身就是 Bourne shell 的擴展。Bourne shell 本身(於 1979 年在 Unix V7 中首次發布)是一個從頭開始的干淨實現,但它並沒有完全脫離 Thompson shell(V1 -> V6 的 shell)並結合了 Mashey shell 的功能。特別是,命令參數仍然由空格分隔,
|
現在是新的管道運算符,但^
仍被支持作為替代(並解釋了為什麼你這樣做[!a-z]
和不這樣做[^a-z]
),$1
仍然是腳本的第一個參數,反斜杠仍然是轉義字元. 許多正則表達式運算符 (^\|$
) 在 shell 中都有自己的特殊含義。Thompson shell 依賴外部實用程序進行通配。當在命令中
sh
找到 unquoted或s 時*
,它將通過.[``?``glob
rm *.txt
最終會執行 glob 為:
["glob", "rm", "*.txt"]
並且 glob 最終會
rm
以匹配該模式的文件列表執行。grep a.\*b *.txt
將執行
glob
為:["glob", "grep", "a.\252b", "*.txt"]
通過在該
*
字元上設置第 8 位來引用上述內容,以防止glob
將其視為萬用字元。glob
然後會在呼叫grep
.用正則表達式做同樣的事情,那就是:
regexp rm '\.txt$'
或者:
regexp rm '^[^.].*\.txt$'
排除點文件。
由於運算符兼作 shell 特殊字元,因此需要轉義運算符,
.
文件名中常見的 regexp 運算符這一事實使得匹配文件名不太合適,並且對於初學者來說很複雜。在大多數情況下,您只需要可以替換一個 ( ) 或任意數量 ( ) 個字元的萬用字元。?``*
現在,不同的 shell 添加了不同的萬用字元。如今,ksh 和 zsh glob(在某種程度上
bash -O extglob
實現了 ksh glob 的子集)在功能上等同於正則表達式,其語法與文件名和目前的 shell 語法一起使用不太麻煩。例如,在zsh
(使用extendedglob 擴展)中,您可以執行以下操作:echo a#.txt
如果您希望(不太可能)匹配由
a
後跟.txt
. 比echo (^a*\.txt$)
(這裡使用大括號將正則表達式運算符與 shell 運算符隔離開來,這可能是 shell 處理它的一種方式)更容易。echo (foo|bar|<1-20>).(#i)mpg
對於基本名稱為 foo、bar 或 1 到 20 的十進制數的 mpg 文件(不區分大小寫)…
ksh93
now 還可以在其 glob 中包含正則表達式(基本的、擴展的、類似 perl 或“增強的”)(儘管它有很多錯誤),甚至提供了一個在 glob 和正則表達式 (printf %R
,printf %P
) 之間轉換的工具:echo ~(Ei:.*\.txt)
以擴展正則表達式匹配(非隱藏)txt 文件,不區分****大小寫。