我有几种模式,其中包括 ** 通配符,让它们成为:
foo/**/bar.xml
foo/**/baz.xml
我有一个示例目录树:
$ tree foo/
foo/
└── deep
├── baz.xml
└── even
└── deeper
└── baz.xml
检查是否满足这些模式的最简单方法是什么?准确地说:返回非零仅当没有时被发现。到目前为止我想到的是使用globstar
and nullglob
。但ls
这里不是一个选项,就好像没有满足任何模式一样,它列出了当前目录。所以我必须利用echo
,像这样:
$ (shopt -s globstar nullglob; echo foo/**/bar.xml foo/**/baz.xml) | grep .
foo/deep/baz.xml foo/deep/even/deeper/baz.xml
$ echo $?
0
与
$ (shopt -s globstar nullglob; echo foo/**/bar.xml foo/**/beque.xml) | grep .
$ echo $?
1
所以是的 - 它有效。但也许我错过了一些东西更简单/更优雅实现这个目标的方法?
答案1
你可以使用一个函数:
non_empty() (($#))
has() (
IFS=
set +o noglob
shopt -s globstar nullglob extglob
shopt -u failglob # which has precedence over nullglob!
for glob do
non_empty $glob && return
done
false
)
if has 'foo/**/bar.xml' 'foo/**/baz.xml'; then
echo matches found
fi
(或has 'foo/**/ba[rz].xml'
或 (与extglob
)has 'foo/**/@(bar|baz).xml'
或has 'foo/**/bar.xml' || has 'foo/**/baz.xml'
)
它停在第一个匹配的 glob 处。
为了使其更高效并在第一个匹配处停止,您可以使用zsh
相反及其Y1
glob 限定符:
has() {
local glob
for glob do
()(($#)) $~glob(NY1) && return
done
false
}
(也将 内联non_empty
到匿名函数中)。
**/
for recursive 确实来自 90 年代初的 zsh,并在那里默认启用(由 bash 复制,但在 2000 年代末默认不启用)。交替是用has 'foo/**/(bar|baz).xml'
in完成的zsh
。
在 中zsh
,您还可以执行以下操作:
alias has='noglob has'
以便能够做到:
if has foo/**/bar.xml foo/**/baz.xml; then
echo matches found
fi
无需引用全局运算符。
答案2
如果可以覆盖位置参数列表:
shopt -s globstar nullglob
set -- foo/**/bar.xml foo/**/baz.xml
if [ "$#" -ne 0 ]; then
echo matches found
fi
这会将位置参数列表设置为与通配模式匹配的名称。由于nullglob
有效,如果未找到匹配项,列表将为空。的值$#
是位置参数列表的长度。
使用您在子 shell 中运行它的想法:
( shopt -s globstar nullglob; set -- foo/**/bar.xml foo/**/baz.xml; [ "$#" -ne 0 ] )
使用数组代替位置参数列表:
shopt -s globstar nullglob
names=( foo/**/bar.xml foo/**/baz.xml )
if [ "${#names[@]}" -ne 0 ]; then
echo matches found
fi