我正在尝试递归地在当前目录的所有子目录下生成所有对象(文件,目录等)的文件名。排除当前目录中的对象。
换句话说,给定:
--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
为什么./**/*~./*失败?更重要的是,如何在子目录中递归地生成元素名称(当前/基本目录中的元素除外)?
谢谢。
((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)中,‘/’
和‘.’
并未像通常在处理时那样受到特殊对待。
((2)x~y
string glob限定词:
e
字符串e
cmd...在执行字符串期间,参数
+
中提供了当前正在测试的文件名;该参数可以更改为要插入列表的字符串,而不是原始文件名。---
REPLY