Shell globbing 排除目录模式
Shell globbing exclude directory patterns
忽略可与 shell glob 关联使用的命令,我想完成从 glob 模式中排除所有目录和子目录中的特定目录模式。
假设我有这个目录结构:
+-- a
+-- a1
+-- assets
+-- a1-1
+-- a1-1.js
+-- a1-2
+-- a1-2.js
+-- a2
+-- a2.js
+-- b
+-- b1
+-- b1-2
+-- b1-2-3
+-- assets
+-- b1-2-3.js
+-- b.js
+-- c
+-- c.js
我想列出文件路径中没有 assets
的所有 js 文件。
到目前为止我尝试过的:
$ shopt -s globstar extglob
$ ls ./**/!(assets)/**/*.js
上面的模式不仅实现了它的目标,甚至还显示了重复的输出。我知道我可以做这样的事情:
$ ls ./**/*.js | grep -v "assets"
或任何其他可以通过管道传输的命令,我只想要一个纯 shell glob 模式。
预期输出:
a/a2/a2.js
b/b.js
c/c.js
问题是 **
在您的情况下匹配太多或太少。特别是,!(assets)
前面的 **
将匹配 a1/assets/
,然后 !(assets)
将匹配 a1-1
,而后面的 **
将不匹配任何内容,因此整体匹配成功
考虑这个简化的例子:
touch a/a1/x.js a/a2/y.js a/a3/z.js
ls ./*/!(a2)/*.js
ls ./**/!(a2)/**/*.js
第一个 ls
按预期工作,第二个没有。
Michael 的回答是正确的。 **
匹配太多(贪婪匹配),包括assets
.
所以,对于这棵树:
.
|-- a
| |-- a1
| | +-- assets
| | |-- a1-1
| | | +-- a1-1.js
| | +-- a1-2
| | +-- a1-2.js
| +-- a2
| +-- a2.js
|-- assets
| +-- xyz.js
|-- b
| |-- b1
| | +-- b1-2
| | +-- b1-2-3
| | |-- assets
| | | +-- b1-2-3.js
| | +-- test
| | |-- test2
| | | +-- test3
| | | +-- test4
| | | +-- test4.js
| | +-- test.js
| +-- b.js
|-- c
| +-- c.js
+-- x.js
.js 文件是:
$ find . -name '*.js'
./x.js
./assets/xyz.js
./a/a2/a2.js
./a/a1/assets/a1-2/a1-2.js
./a/a1/assets/a1-1/a1-1.js
./c/c.js
./b/b.js
./b/b1/b1-2/b1-2-3/test/test2/test3/test4/test4.js
./b/b1/b1-2/b1-2-3/test/test.js
./b/b1/b1-2/b1-2-3/assets/b1-2-3.js
有一个 bash 变量 GLOBIGNORE
可以完全按照您的意愿执行。
所以,这会起作用:
$ GLOBIGNORE='**/assets/**:assets/**:**/assets'
$ ls -1 **/*.js
a/a2/a2.js
b/b1/b1-2/b1-2-3/test/test2/test3/test4/test4.js
b/b1/b1-2/b1-2-3/test/test.js
b/b.js
c/c.js
x.js
这可能是您开始使用 zsh
的最佳时机 :-) 只需使用 ~
:
即可删除匹配项
% setopt extended_glob
% ls -1 **/*.js~*assets*
a/a2/a2.js
b/b.js
c/c.js
忽略可与 shell glob 关联使用的命令,我想完成从 glob 模式中排除所有目录和子目录中的特定目录模式。
假设我有这个目录结构:
+-- a
+-- a1
+-- assets
+-- a1-1
+-- a1-1.js
+-- a1-2
+-- a1-2.js
+-- a2
+-- a2.js
+-- b
+-- b1
+-- b1-2
+-- b1-2-3
+-- assets
+-- b1-2-3.js
+-- b.js
+-- c
+-- c.js
我想列出文件路径中没有 assets
的所有 js 文件。
到目前为止我尝试过的:
$ shopt -s globstar extglob
$ ls ./**/!(assets)/**/*.js
上面的模式不仅实现了它的目标,甚至还显示了重复的输出。我知道我可以做这样的事情:
$ ls ./**/*.js | grep -v "assets"
或任何其他可以通过管道传输的命令,我只想要一个纯 shell glob 模式。
预期输出:
a/a2/a2.js
b/b.js
c/c.js
问题是 **
在您的情况下匹配太多或太少。特别是,!(assets)
前面的 **
将匹配 a1/assets/
,然后 !(assets)
将匹配 a1-1
,而后面的 **
将不匹配任何内容,因此整体匹配成功
考虑这个简化的例子:
touch a/a1/x.js a/a2/y.js a/a3/z.js
ls ./*/!(a2)/*.js
ls ./**/!(a2)/**/*.js
第一个 ls
按预期工作,第二个没有。
Michael 的回答是正确的。 **
匹配太多(贪婪匹配),包括assets
.
所以,对于这棵树:
.
|-- a
| |-- a1
| | +-- assets
| | |-- a1-1
| | | +-- a1-1.js
| | +-- a1-2
| | +-- a1-2.js
| +-- a2
| +-- a2.js
|-- assets
| +-- xyz.js
|-- b
| |-- b1
| | +-- b1-2
| | +-- b1-2-3
| | |-- assets
| | | +-- b1-2-3.js
| | +-- test
| | |-- test2
| | | +-- test3
| | | +-- test4
| | | +-- test4.js
| | +-- test.js
| +-- b.js
|-- c
| +-- c.js
+-- x.js
.js 文件是:
$ find . -name '*.js'
./x.js
./assets/xyz.js
./a/a2/a2.js
./a/a1/assets/a1-2/a1-2.js
./a/a1/assets/a1-1/a1-1.js
./c/c.js
./b/b.js
./b/b1/b1-2/b1-2-3/test/test2/test3/test4/test4.js
./b/b1/b1-2/b1-2-3/test/test.js
./b/b1/b1-2/b1-2-3/assets/b1-2-3.js
有一个 bash 变量 GLOBIGNORE
可以完全按照您的意愿执行。
所以,这会起作用:
$ GLOBIGNORE='**/assets/**:assets/**:**/assets'
$ ls -1 **/*.js
a/a2/a2.js
b/b1/b1-2/b1-2-3/test/test2/test3/test4/test4.js
b/b1/b1-2/b1-2-3/test/test.js
b/b.js
c/c.js
x.js
这可能是您开始使用 zsh
的最佳时机 :-) 只需使用 ~
:
% setopt extended_glob
% ls -1 **/*.js~*assets*
a/a2/a2.js
b/b.js
c/c.js