Zsh glob:对象在目录下递归,不包括当前/基本目录

问题描述 投票:2回答:1

我正在尝试递归地在当前目录的所有子目录下生成所有对象(文件,目录等)的文件名。排除当前目录中的对象。

换句话说,给定:

--dir1 --dir2.1
|      | dir2.2 --file3.1
|      --file2.1
--file1

我想生成:

./dir2.1
./dir2.2
./dir2.2/file3.1
./file2.1

我已经设置了EXTENDED_GLOB选项,并且我认为以下模式可以解决问题:

./**/*~./*

但它返回:

zsh: no matches found: ./**/*~./*

我不知道问题是什么,它应该起作用。

./**/*给出:

./dir1
./dir2.1
./dir2.2
./dir2.2/file3.1
./file2.1
./file1

和./*给出:

./dir1
./file1

为什么./**/*~./*失败?更重要的是,如何在子目录中递归地生成元素名称(当前/基本目录中的元素除外)?

谢谢。

zsh glob
1个回答
0
投票

((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

排除模式./*匹配由全局./**/*生成的所有内容,因此zsh最终产生“未找到匹配项”。 (zsh不会为~y部分生成文件名。)

我们可以使排除模式更精确/更复杂,以排除当前目录中的元素。这样它就以./开头,并且具有/以外的一个或多个字符。

% print -l ./**/*~./[^/]## ;# use '~./[^/]##' rather than '~./*'
./dir1/dir2.1
./dir1/dir2.2
./dir1/dir2.2/file3.1
./dir1/file2.1

然后,要去除当前目录分量/dir1,我们可以使用(2)e string 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

或使用通常的数组/替换而不是e string 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)x~y string glob限定词:

e 字符串ecmd

...在执行字符串期间,参数+中提供了当前正在测试的文件名;该参数可以更改为要插入列表的字符串,而不是原始文件名。

--- REPLY

© www.soinside.com 2019 - 2024. All rights reserved.