假设我有两个可能的路径,我想在 Linux 机器上列出目录和文件:
/some/path1/
/some/path2/
如果我在 中执行以下操作tcsh
,我会得到0
退出代码,如果至少有一个path1
或path2
存在:
ls -d /some/{path1,path2}/*
但是,如果我在 中执行完全相同的操作bash
,我会得到2
退出代码,并显示stderr
消息报告path1
不存在(如果 path1 是不存在的那个)。
在这种情况下,我该如何bash
表现tcsh
?如果至少存在一条路径,ls
我可以要求它回馈吗?0
如果两者都不存在,我确实期望非零代码,这就是tcsh
回馈。
您的大部分问题已经在为什么 nullglob 不是默认值?.
要记住的一件事是:
在
csh
////tcsh
(但不是zsh
,见下文bash
)中是一样的:ksh
fish
由于大括号扩展是在glob 扩展之前执行的(不是作为.
/some/pathx/*
ls
bash
,就像ksh
它主要复制的一样,它继承了 Bourne shell 引入的错误功能,即当 glob 模式与任何文件都不匹配时,它按原样传递,字面上作为命令的参数。因此,在这些 shell 中,如果
/some/path1/*
匹配至少一个文件并且/some/path2/*
不匹配任何文件,ls
则将使用-d
、/some/path1/file1
和/some/path1/file2
文字/some/path2/*
作为参数调用。由于/some/path2/*
文件不存在,ls
会报错。csh
其行为类似于早期的 Unix 版本,其中 glob 不是由 shell 执行,而是由/etc/glob
辅助实用程序(将它们的名称命名为 glob)执行。该助手将在调用命令之前执行 glob 扩展,如果所有glob 模式都无法匹配任何文件,则在No match
不运行命令的情况下报告错误。否则,只要至少有一个匹配的 glob,所有不匹配的都会被简单地删除。因此,在我们上面的示例中,使用
csh
/tcsh
或古老的 Thompson shell 及其/etc/glob
助手,ls
将使用-d
,/some/path1/file1
和/some/path1/file2
only 调用,并且可能会成功(只要/some/path1
是可搜索的)。zsh
既是 Korn-like 又是 csh-like shell。它没有 Bourne shell 的错误功能,即不匹配的 glob 按原样传递¹,但默认情况下比csh
这更严格,所有失败的 glob 都被视为致命错误。因此zsh
,在默认情况下,如果其中一个/some/path1/*
或/some/path2/*
(或两者)不匹配,则该命令被中止。可以使用option²在bash
shell中启用类似的行为。failglob
这使得行为更加可预测/一致,但意味着当您想要将多个 glob 扩展传递给命令并且不希望它失败时,只要其中一个 glob 成功,您就会遇到该问题。但是,您可以设置
cshnullglob
选项以获得类似 csh 的行为(或emulate csh
)。这可以通过使用匿名函数在本地完成:
或者只是使用一个子shell:
但是,在这里,您可以使用交替 glob 运算符来使用与所有它们匹配的一个,而不是使用两个 glob:
在这里,您甚至可以这样做:
在
bash
中,您可以启用extglob
bash 选项以支持 ksh 的扩展 glob 运算符的子集,包括交替:现在,由于从 Bourne shell 继承的错误功能,如果该 glob 不匹配任何文件,
/some/@(path1|path2)/*
将按原样传递给ls
并ls
最终列出一个名为literal 的文件/some/@(path1|path2)/*
,因此您还希望启用failglob
选项谨防:或者,您可以使用
nullglob
选项(从bash
复制zsh
)将所有不匹配的 glob 扩展为空。但:在命令的特殊情况下是错误的
ls
,如果没有传递任何参数列表.
。但是,您可以使用nullglob
将 glob 扩展存储到数组中,并且仅ls
在数组成员为非空时将其作为参数调用:在
zsh
中,而不是全局启用,您可以使用glob 限定符(启发 ksh 的,尚未复制)nullglob
在每个 glob 的基础上启用它,并再次使用匿名函数而不是数组:(N)
~(N)
bash
shell 现在的
fish
行为类似于zsh
失败的 glob 导致错误的地方,除了当 glob 与for
,set
(用于分配数组)一起使用或count
它以某种nullglob
方式运行时。此外,在 中
fish
,虽然大括号扩展本身不是 glob 运算符,但它是作为通配的一部分完成的,或者当大括号扩展与通配相结合并且至少可以返回一个元素时,至少一个命令不会中止。所以,在
fish
:最终会表现得像 in
csh
。甚至:
如果不匹配而不是失败(在这种情况下与 csh 表现不同),将导致单独
ls
调用。-d
/xx*
无论如何,如果只是
print
匹配文件路径,则不需要ls
. 在zsh
中,您可以使用其print
内置的列打印:将在 3 列上打印路径
r
aw(如果与N
ullglob glob 限定符不匹配,则不打印任何内容)。相反,如果您想检查这两个目录中是否至少有一个非隐藏文件,您可以执行以下操作:
或者使用一个函数:
或者有一个功能:
(
Y1
作为找到第一个文件后停止的优化)请注意
has_non_hidden_files
,对于用户无法读取的目录(无论它们是否有文件),它们会(默默地)返回 false。在中,您可以通过其特殊变量zsh
检测到这种情况。$ERRNO
¹ Bourne 行为(由 POSIX 指定)可以
zsh
通过 doemulate sh
或emulate ksh
或 with启用set +o nomatch
² 请注意,当 glob 不匹配时,具体取消的行为存在显着差异,这种
fish
行为通常更明智,也bash -O failglob
可能是最糟糕的只需阅读Stéphane 的回答。
发生这种情况是因为如何
bash
对待不匹配任何内容的 glob。要获得您在 中观察到的相同行为tcsh
,您需要启用 bash 的nulglob
选项。来自man bash
:启用
nullglob
,shopt -s nullglob
您将获得预期的行为: