Excluding a directory name in a zsh recursive glob
Solution 1
Zsh's extended glob operators support matching over /
(unlike ksh's, even in zsh's implementation). Zsh's **/
is a shortcut for (*/)#
(*/
repeated 0 or more times). So all I need to do is replace that *
by ^.svn
(anything but .svn
).
print -l (^.svn/)#
Neat!
Solution 2
While ksh93
globbing is nowhere near zsh
's even with the globstar
option, in ksh93
, it can be achieved with:
set -o globstar
FIGNORE='@(.|..|.svn)'
printf '%s\n' **/
Related videos on Youtube
Gilles 'SO- stop being evil'
Updated on September 18, 2022Comments
-
Gilles 'SO- stop being evil' over 1 year
I'm running zsh on Linux under
setopt extended_glob ksh_glob glob_dots
. I'm looking for something easy to type on the command line, with no portability requirements. I'm looking at a source code tree, with no “weird” file names (e.g. no\
in file names, no file name beginning with-
).Either of the following commands print the list of subdirectories of the current directory recursively:
find -type d print -l **/*/
This is actually an svn checkout:
$ find -type d ./.deps ./.svn ./.svn/text-base ./.svn/prop-base ./.svn/tmp ./.svn/tmp/text-base ./.svn/tmp/prop-base ./.svn/tmp/props ./.svn/props ./lib/.svn ./lib/.svn/text-base ./lib/.svn/prop-base ./lib/.svn/tmp ./lib/.svn/tmp/text-base ./lib/.svn/tmp/prop-base ./lib/.svn/tmp/props ./src/.svn ./src/.svn/text-base ./src/.svn/prop-base ./src/.svn/tmp ./src/.svn/tmp/text-base ./src/.svn/tmp/prop-base ./src/.svn/tmp/props
I want to exclude the
.svn
directories and their subdirectories which are present in every directory. It's easy withfind
:find -type d -name .svn -prune -o -print
Can I do this with a short zsh glob? Ignoring dot files comes close (I need to do it explicitly because I have
glob_dots
set):print -l **/*(/^D)
But this isn't satisfactory because it hides the
.deps
directory, which I do want to see. I can filter out the paths containing.svn
:print -l **/*~(*/|).svn(|/*)(/)
But that's barely shorter than
find
(so what am I using zsh for?). I can shorten it toprint -l **/*~*.svn*(/)
, but that also filters out directories calledhello.svn
. Furthermore, zsh traverses the.svn
directories, which is a bit slow on NFS or Cygwin.Is there a convenient (as in easy to type) way to exclude a specific directory name (or even better: an arbitrary pattern) in a recursive glob?
-
Stéphane Chazelas about 11 yearsWhy would you spend some time finding out whether your files have or have not dashes or backslashes (and let other readers find out by themselves when they can't guarantee that) instead of just write the bulletproof right thing like
printf '%s\n' **/*
orprint -rl -- **/*
? -
Gilles 'SO- stop being evil' about 11 years@StephaneChazelas In a script, sure. But on the command line, saving 4 characters is worth it. In my real-world situation, I didn't need to find out about file names, I already knew (build trees tend to have tame file names).
-
-
Andy Fowler about 8 yearsThis is a great answer — though it requires
setopt EXTENDED_GLOB
. Without it, you'll getzsh: bad pattern: [...]
. -
brainplot about 5 yearsI'm having trouble applying your answer to my use case. I'm trying to match the set of
CMakeLists.txt
files scattered in various subdirectory, excluding all directories starting withcmake-*
from being searched. I tried(^cmake-*)**/CMakeLists.txt#
; it works, but it doesn't include theCMakeLists.txt
in my working directory (i.e. it finds those located in subdirectories exclusively). How can I include it? Thanks, and great answer! -
Gilles 'SO- stop being evil' about 5 years@brainplot If you want to exclude
cmake-*
at any level, then like in my answer, use(^cmake-*/)#CMakeLists.txt
. If you only want to excludecmake-foo/bar/CMakeLists.txt
but notbaz/cmake-qux/CMakeLists.txt
, you need a different approach:**/CMakeLists.txt~cmake-*/*
. -
brainplot about 5 years@Gilles all the
cmake-*
folders I wanted to exclude are in my current directory and I wanted to exclude them all. I tried your first solution and it worked. Thank you!