如何检查 bash 中是否存在多个模式

如何检查 bash 中是否存在多个模式

我有几种模式,其中包括 ** 通配符,让它们成为:

foo/**/bar.xml
foo/**/baz.xml

我有一个示例目录树:

$ tree foo/
foo/
└── deep
    ├── baz.xml
    └── even
        └── deeper
            └── baz.xml

检查是否满足这些模式的最简单方法是什么?准确地说:返回非零仅当没有时被发现。到目前为止我想到的是使用globstarand 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相反及其Y1glob 限定符:

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

相关内容