為什麼 nullglob 不是預設值?
在大多數 shell
nullglob
中不是預設設置。這意味著,例如,如果您執行此命令ls *
在一個空目錄中,它會將
*
glob 擴展為一個文字*
,而不是一個空的參數列表。有一些方法可以改變這種行為,這樣*
在一個空目錄中將返回一個空的參數列表,這看起來更直覺。
nullglob
那麼,預設情況下禁用是否有原因?如果是這樣,那是什麼原因?
該
nullglob
選項(順便說一句,這是一項zsh
發明,幾年後才添加到bash
(2.0
)中)在許多情況下並不理想。並且ls
是一個很好的例子:ls *.txt
或更正確的等價物:
ls -- *.txt
(列出名稱以 結尾的非隱藏目錄的內容
.txt
,或具有相同名稱模式的非目錄文件)
nullglob
如果沒有文件匹配, on 將不帶任何參數執行,如果沒有文件匹配,則將ls
其視為ls -- .
(列出目前目錄),這可能比ls
使用文字*.txt
作為參數呼叫更糟糕¹。大多數文本實用程序都會遇到類似的問題:
grep foo *.txt
foo
如果沒有txt
文件,將在標準輸入上查找。如果 glob 不匹配,一個更明智的預設設置,以及 csh、tcsh、zsh 或 fish 2.3+(以及早期的 Unix shell)之一是完全取消命令。
bash
(從版本 3 開始)有一個failglob
選項(這個討論很有趣,因為與ATash
&Tksh
或不支持 options² 的本地範圍相反,該選項在全域啟用時會破壞一些事情,比如 bash 完成功能)。zsh``bash
請注意, csh 和 tcsh 與 略有不同
zsh
,fish
或者bash -O failglob
在以下情況下:ls -- *.txt *.html
您需要所有 glob 不匹配才能取消命令。例如,如果有一個 txt 文件而沒有 html 文件,則變為:
ls -- file.txt
您可以通過以下方式獲得該行為,
zsh
儘管set -o cshnullglob
更明智的方法zsh
是使用如下的 glob:ls -- *.(txt|html)
在
zsh
andksh93
中,您還可以在每個 glob 的基礎上應用nullglob,這比修改全域設置要明智得多:files=(*.txt(N)) # zsh files=(~(N)*.txt) # ksh93
如果沒有
txt
文件,則將創建一個空數組,而不是使命令失敗並出現錯誤(或使其成為*.txt
具有其他 shell 的一個文字參數的數組)。
fish
2.3 之前的版本可以正常工作,bash -O nullglob
但是當 glob 不匹配時互動時會發出警告。從 2.3 開始,它zsh
的工作方式與for
,set
或count
.現在,在歷史記錄中,行為實際上被 Bourne shell*破壞了。*在早期版本的 Unix 中,萬用字元是通過
/etc/glob
幫助程序完成的,該幫助程序的行為類似於csh
:如果沒有任何 glob 匹配任何文件,它將使命令失敗,否則將刪除不匹配的 glob。所以我們今天的情況是由於在 Bourne shell 中做出了一個錯誤的決定。
請注意,Bourne shell(和 C shell)帶有另一個新的 Unix 特性:環境。這意味著變數擴展(它的前身只有
$1
,$2
… 位置參數)。Bourne shell 還引入了命令替換。Bourne shell 的另一個糟糕的設計決策是在變數擴展和命令替換時執行通配(和拆分)(可能是為了與 Thompson shell 向後兼容,如果包含萬用字元
echo $1
仍會呼叫它(它更像是預處理器宏擴展)在那裡,就像在擴展的值被再次解析為 shell 程式碼一樣))。/etc/glob``$1
不匹配的失敗 glob 意味著例如:
pattern='a.*b' grep $pattern file
將使命令失敗(除非目前目錄中有一些
a.whateverb
文件)。csh
(它也會在變數擴展時執行 globbing)在這種情況下確實會使命令失敗(我認為這比在那裡留下一個休眠的 bug 更好,即使它不像 inrc
/zsh
/fish
… )。¹
ls
可能會報告*.txt
文件不存在的錯誤,除非它是在間隔中創建的,或者目前目錄恰好可搜尋但不可讀並且該文件或目錄存在。mkdir -p '*.txt/wtf'; chmod a=,u=wx .
例如嘗試之後² 4.4 版在這方面進行了一些改進,因為
set -o
可以將設置的選項設置為在 Almquist shell 中類似的函式的本地local -
,但這不適用於bash
設置的第二組選項shopt