为什么nullglob不是默认值?


61

在大多数shell nullglob中不是默认值。这意味着,例如,如果您运行此命令

ls *

在一个空目录中,它将把*glob 扩展为一个literal *,而不是一个空的参数列表。有多种方法可以更改该行为,以便*在一个空目录中返回一个空参数列表,这似乎更直观。

那么,是否有nullglob默认原因默认禁用的原因?如果是这样,那是什么原因?


13
曾经有人做出了错误的选择,结果变成了“我们一直这样做”。在软件世界中(不仅是)非常普遍的现象。
PSkocik

4
最初的原因是nullglob选项当时不存在。因此,为了保持向后兼容性,默认情况下必须将其禁用。
下午15年

3
尽管它没有特别提到null的glob,但此答案提供了有关glob的一些历史记录:unix.stackexchange.com/a/136409/22724
StrongBad 2015年

4
我得到一些印象,认为默认情况下应该启用nullglob应该很明显。我不认为这很明显。扩展是在命令调用之前在shell中发生的,这意味着扩展到什么都不是比glob保持不变的直观行为。
kojiro

2
@kojiro对谁不太直观?任何熟悉* NIX shell的人都知道这*是一个问题,并会扩展到所有现有文件。在空目录glob被“扩展”为文字的情况下,有什么特殊的情况如何“直观” *
凯尔·斯特兰德

Answers:


78

在很多情况下,该nullglob选项(BTW是一项zsh发明,仅在bash2.0)之后增加了几年)并不理想。这ls是一个很好的例子:

ls *.txt

或更正确的等价形式:

ls -- *.txt

随着nullglob上可以运行ls不带参数,它是为处理ls -- .(列出当前目录),如果没有文件匹配,这可能比打电话更差ls与文字*.txt作为参数。

大多数文本实用程序都存在类似的问题:

grep foo *.txt

foo如果没有txt文件,将在stdin上查找。

更明智的默认设置是csh,tcsh,zsh或fish 2.3+(以及早期Unix shell)之一,如果glob不匹配,则完全取消该命令。

bash(自版本3起)有一个failglob选项(此讨论很有趣,因为与ashAT&T ksh或相反zshbash它不支持选项的本地范围(尽管将在4.4中进行更改),该选项在全局启用后确实会破坏一些功能例如bash补全功能)。

需要注意的是CSH和tcsh从略有不同zshfishbash -O failglob在类似的情况:

ls -- *.txt *.html

您需要所有glob都不匹配的地方才能取消命令。例如,如果只有一个txt文件而没有html文件,则该文件将变为:

ls -- file.txt

你可以得到与行为zsh具有setopt cshnullglob虽然是更明智的方式做到这一点的zsh是使用一个像水珠:

ls -- *.(txt|html)

zsh和中ksh93,您还可以基于每个glob 来应用nullglob,这比修改全局设置要聪明得多:

files=(*.txt(N))  # zsh
files=(~(N)*.txt) # ksh93

如果没有txt文件,则会创建一个空数组,而不是使命令失败并返回错误(或使其成为具有一个*.txt带有其他Shell的文字参数的数组)。

fish2.3之前的版本可以像这样工作,bash -O nullglob但在全局不匹配时进行交互时会发出警告。从2.3开始,它的工作方式与,或中zsh使用的glob相同。forsetcount

现在,根据历史记录,该行为实际上 Bourne外壳破坏了。在Unix的早期版本中,通配符是通过/etc/glob辅助程序完成的,并且该辅助程序的行为类似于csh:如果所有通配符都不匹配任何文件,则该命令将失败,否则将不匹配的通配符删除。

因此,我们今天的情况是由于Bourne Shell中的错误决定所致。

请注意,Bourne shell(和C shell)带有Unix的另一个新功能:环境。这意味着变量扩展(它的前身只用了$1$2...位置参数)。Bourne shell还引入了命令替换。

Bourne shell的另一个糟糕的设计决策是在变量扩展和命令替换时执行globing(和拆分)(可能是为了与Thompson shell向后兼容,如果包含通配符,Thompson shell echo $1仍将调用它(更像是预处理器宏扩展)在那里,因为再次将扩展值解析为shell代码))。/etc/glob$1

不匹配的glob失败意味着例如:

pattern='a.*b'
grep $pattern file

将使命令失败(除非a.whateverb当前目录中有一些文件)。csh(它还会在变量扩展时执行globing)在这种情况下确实会使命令失败(而且我认为它比在其中保留休眠的bug更好,即使它不像in中那样根本不进行globing也是如此zsh)。


还有另一个可用性问题:nullglob似乎会中断制表符的完成(启用后按Tab键不会执行任何操作)。
凯尔·斯特兰德

1
它可以使用在了nullglob一个每水珠基础上使用bash -虽然语法不优雅的的zshfiles=$(shopt -s nullglob;echo *.txt)
乔恩·纳利

2
@JonNalley,它将文件名的串联(带空格)(可以通过转换xpg_echo)存储到标量变量中。您需要readarray -td '' files < <(shopt -s nullglob; printf '%s\0' *.txt)使用bash4.4或更高版本或(shopt -s nullglob; printf '%s\0' *.txt) | xargs -r0 cmdGNU之类xargs的东西,才能完全使用任意文件名。或者,仍然使用bash4.4,使用帮助程序功能local -(从25年后的ash中复制)用于选项的本地范围。
斯特凡Chazelas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.