(1)x~y glob 运算符使用 y 作为 shell 的常规模式匹配而不是文件名生成,因此 ./**/*~./* 给出“找不到匹配项”:
% print -l ./**/*~./*
;# ./dir1 # <= './*' matches, so exclude this entry
;# ./dir2.1 # <= './*' matches, so exclude this entry
;# .. # ditto...
;# => finally, no matches found
排除模式 ./* 匹配 glob ./**/* 生成的所有内容,因此 zsh 最终产生“未找到匹配项”。 (zsh 不会为~y 部分生成文件名。)
我们可以使排除模式更精确/复杂一点,以排除当前目录中的元素。这样它以./ 开头,并且有一个或多个不是/ 的字符。
% print -l ./**/*~./[^/]## ;# use '~./[^/]##' rather than '~./*'
./dir1/dir2.1
./dir1/dir2.2
./dir1/dir2.2/file3.1
./dir1/file2.1
然后,为了去除当前目录组件 /dir1,我们可以使用 (2)estring glob 限定符,这样它会删除第一次出现的 /[^/]## (例如/dir1):
# $p for avoiding repetitive use of the exclusion pattern.
% p='./[^/]##'; print -l ./**/*~${~p}(e:'REPLY=${REPLY/${~p[2,-1]}}':)
./dir2.1
./dir2.2
./dir2.2/file3.1
./file2.1
或者使用通常的数组/替换而不是 estring glob 限定符来剥离它:
% p='./[^/]##'; a=(./**/*~${~p}) ; a=(${a/${~p[2,-1]}}); print -l $a
./dir2.1
./dir2.2
./dir2.2/file3.1
./file2.1
最后,迭代当前目录的目录也可以完成这项工作:
a=(); dir=;
for dir in *(/); do
pushd "$dir"
a+=(./**/*)
popd
done
print -l $a
#=> ./dir2.1
./dir2.2
./dir2.2/file3.1
./file2.1
这里有一些 zsh 文档。
(1)x~y全局运算符:
x~y
(需要设置EXTENDED_GLOB。)匹配任何匹配模式x但不匹配y的东西。它的优先级低于除‘|’ 之外的任何运算符,因此‘*/*~foo/bar’ 将搜索‘.’ 中所有目录中的所有文件,然后排除‘foo/bar’ 如果有这样的匹配。 ‘foo~bar~baz’ 可以排除多个模式。在排除模式 (y) 中,‘/’ 和 ‘.’ 没有像在通配符中通常那样进行特殊处理。
--- zshexpn(1), x~y, Glob Operators
(2)e字符串 全局限定符:
e字符串
+cmd
...
在执行字符串期间,当前正在测试的文件名在参数REPLY 中可用;该参数可以更改为要插入到列表中的字符串,而不是原始文件名。
--- zshexpn(1), estring, Glob Qualifiers