在大多数shell nullglob
中不是默认值。这意味着,例如,如果您运行此命令
ls *
在一个空目录中,它将把*
glob 扩展为一个literal *
,而不是一个空的参数列表。有多种方法可以更改该行为,以便*
在一个空目录中返回一个空参数列表,这似乎更直观。
那么,是否有nullglob
默认原因默认禁用的原因?如果是这样,那是什么原因?
*
是一个问题,并会扩展到所有现有文件。在空目录glob被“扩展”为文字的情况下,有什么特殊的情况如何“直观” *
?
在大多数shell nullglob
中不是默认值。这意味着,例如,如果您运行此命令
ls *
在一个空目录中,它将把*
glob 扩展为一个literal *
,而不是一个空的参数列表。有多种方法可以更改该行为,以便*
在一个空目录中返回一个空参数列表,这似乎更直观。
那么,是否有nullglob
默认原因默认禁用的原因?如果是这样,那是什么原因?
*
是一个问题,并会扩展到所有现有文件。在空目录glob被“扩展”为文字的情况下,有什么特殊的情况如何“直观” *
?
Answers:
在很多情况下,该nullglob
选项(BTW是一项zsh
发明,仅在bash
(2.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
选项(此讨论很有趣,因为与ash
AT&T ksh
或相反zsh
,bash
它不支持选项的本地范围(尽管将在4.4中进行更改),该选项在全局启用后确实会破坏一些功能例如bash补全功能)。
需要注意的是CSH和tcsh从略有不同zsh
,fish
或bash -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的文字参数的数组)。
fish
2.3之前的版本可以像这样工作,bash -O nullglob
但在全局不匹配时进行交互时会发出警告。从2.3开始,它的工作方式与,或中zsh
使用的glob相同。for
set
count
现在,根据历史记录,该行为实际上被 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键不会执行任何操作)。
files=$(shopt -s nullglob;echo *.txt)
xpg_echo
)存储到标量变量中。您需要readarray -td '' files < <(shopt -s nullglob; printf '%s\0' *.txt)
使用bash
4.4或更高版本或(shopt -s nullglob; printf '%s\0' *.txt) | xargs -r0 cmd
GNU之类xargs
的东西,才能完全使用任意文件名。或者,仍然使用bash4.4,使用帮助程序功能local -
(从25年后的ash中复制)用于选项的本地范围。