如何使用 zsh globbing 指定路径组件是可选的?

如何使用 zsh globbing 指定路径组件是可选的?

假设我有以下目录结构:

% tree foo 
foo
├── bar
│   └── baz
└── baz

怎样才能优雅地搭配两者foo/bar/baz foo/baz?即如何使bar/路径的组成部分可选?

foo/*/baz仅匹配foo/bar/baz,因为foo//baz不是有效的扩展。我尝试使用可选组,但是:

% set -o KSH_GLOB
% echo foo/?(bar)/baz

也仅foo/bar/baz出于相同的原因扩展为(foo//baz不是有效的扩展)。/根据 ,在组内包含 a通常是非法的zshexpn(1),因此 egfoo/?(bar/)baz会引发错误。

这看起来并不是一个特别奇怪的案例,但我在 Google 搜索或 SE 搜索上找不到任何相关内容。

答案1

这是 ksh 和 zsh glob 的一个痛点。您可以使用大括号:

echo foo{,/bar}/baz
echo foo{,/*}/baz    # you probably don't want this one, see below

然而,大括号不是整体,它们是否足够接近取决于您正在做什么。

主要问题是哪些文件保证存在以及如果某些文件不存在您希望发生什么。foo{,/bar}/baz扩展为两个单词foo/baz以及foo/bar/baz文件是否存在。使用通配符,默认情况下,foo{,/*}/baz如果没有匹配的文件foo/*/baz,即使foo/baz存在,也会出错。你可以用以下方法解决这个问题N 通配标志:

echo foo{/*,}/baz(N)

这将扩展为匹配的现有文件列表foo/*/baz(即使该列表为空),foo/baz如果存在则后跟。这相当于foo/(*/|)/baz(N)如果它有效的话你会得到什么。另一方面,没有简单的等效方法foo/(*/|)/baz:如果您希望在没有匹配项时出现错误,则必须自己执行此操作,例如

bazes=(foo{,/*}/baz(N))
if (($#bazes == 0)); then echo >&2 "No bazes are belong to us"; return 1; fi

大括号和 glob 之间的另一个区别是大括号中的顺序决定了扩展的顺序。例如,echo foo{,/*}/baz(Nom)始终将其放在第一位foo/baz,无论其修改时间如何。

答案2

如果您不小心匹配 >1 个“可选”路径名组件,这对您来说并不重要,您可以执行以下操作:

% set -o EXTENDED_GLOB
% echo foo/(bar/)#baz

这将匹配foo/barfoo/bar/baz,但它也将匹配foo/bar/bar/baz等。

答案3

您没有明确说明是否想要这两个地方的匹配文件仅有的, 或者全部他们在树上。如果可以选择匹配所有这些,您可以使用**

% mkdir -p foo/{bar,asdf/asdf,}/baz
% echo foo/**/baz       
foo/asdf/asdf/baz foo/bar/baz foo/baz

它也适用于 Bash(globstar启用):

$ shopt -s globstar
$ echo foo/**/baz       
foo/asdf/asdf/baz foo/bar/baz foo/baz

(zsh 和 Bash 之间存在一些警告/差异**。树中的符号链接。)

相关内容