Disregarding commands that can be used in association with shell globs, I would like to accomplish excluding specific directory patterns across all directories and sub-directories from a glob pattern.
Let's say I have this directory structure:
+-- 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
I would like to list all js files that do not have assets
in their file paths.
What I've tried so far:
$ shopt -s globstar extglob
$ ls ./**/!(assets)/**/*.js
Not only did the pattern above achieve its goal, it even shows duplicate outputs. I am aware that I can do something like this:
$ ls ./**/*.js | grep -v "assets"
Or any other commands that can be piped, I simply want a pure shell glob pattern.
Expected Output:
a/a2/a2.js
b/b.js
c/c.js
Michael's answer is right. **
matches too much (greedy match), including assets
.
So, with this tree:
.
|-- 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
The .js files are:
$ 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
There is a bash variable GLOBIGNORE
to do exactly what you are trying to do.
So, this would work:
$ 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
The problem is that **
matches too much or too little in your case. In particular, the **
preceding !(assets)
will match a1/assets/
, then !(assets)
will match a1-1
and the following **
will match nothing, so that the overall match succeeds.
Consider this simplified example:
touch a/a1/x.js a/a2/y.js a/a3/z.js
ls ./*/!(a2)/*.js
ls ./**/!(a2)/**/*.js
The first ls
works as intended, the second does not.
This might be the perfect time for you to start using zsh
:-) Removing matches is simply done with ~
:
% setopt extended_glob
% ls -1 **/*.js~*assets*
a/a2/a2.js
b/b.js
c/c.js
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With