12

I need to list all the sub directories of a directory excluding those that match any of a list given as an argument: "SUBDIR1,SUBDIR2,...,SUBDIRN".

I came with the solution (from many sources) of using ls with a glob pattern. To test the concept I tried in the command line the following sequence which seems to work:

DIR="/path/to/dirs"
EXCLUDELIST="subdir1,subdir2"
#transform in a glob pattern for excluding given subdirectories
EXCLUDE="!(${EXCLUDELIST//,/|})"
LIST=$(cd $DIR && ls -l -d $EXCLUDE | grep -E "(^d)" | awk '{print $9}')

However, when I put it in a bash script unmodified I get this error

ls: cannot access !(subdir1|subdir2): No such file or directory

What am I doing wrong when putting this code in the script?

2 Answers 2

5

Dennis told you why your script is failing but I'd like to suggest an easier (and safer) way of doing this. Parsing ls is almost always a bad idea, it can easily break on file names with spaces or new lines or other strange characters and is not portable across LOCALE settings. Also, your command is very complex, involving multiple steps. Why not do it all in find?

DIR="/path/to/dirs"
EXCLUDELIST="subdir1,subdir2"
## Build the regular expression
EXCLUDE="${EXCLUDELIST//,/|.*}"
LIST=$(find "$DIR" -type d -regextype posix-awk -not -regex ".*$EXCLUDE.*")

By the way, your script will fail as you have it written because you build the glob before cd-ing into $DIR so it will be built with respect to the contents of your current directory.

1
  • Thanks, it works, only has to add -printf "%f\n" to get only the subdir name (not the whole path). By the way, cd-ing before the glob is by design and it works when in interactive mode. Feb 11, 2014 at 9:01
14

Interactive and non-interactive bash shells do not behave the same. One of the many differences is that the shell option extglob is enabled by default for interactive shells (at least in my version of bash), but not for non-interactive ones.

To fix your script, enable extglob with the following command:

shopt -s extglob
3
  • Activating this option made the shell to expand the glob before executing the ls command: ls -1 -d subdir3 subdir4 subdir5 ..... subdirn where the list of subdir is the actual expansion of the glob. Feb 11, 2014 at 8:52
  • Yes, that's how globs work. I don't think I understand what you're implying.
    – Dennis
    Feb 11, 2014 at 12:14
  • the shopt command works in #!/bin/bash scripts but not in #!/bin/sh (which on my RHEL6 system is actually bash, but using that name tells it to use different behavior). My google-fu is failing me, i can't find the equivalent bourne shell command.
    – Dan Pritts
    Jul 20, 2016 at 16:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.