I'm transitioning from being a long time tcsh user to a new bash user (it's way overdue). I wrote a lot of foreach loops in tcsh on the fly on a regular basis, so I learned the syntax for bash's for loops as a substitute, but was surprised when non-matching glob patterns got passed through the loop as literal strings. I searched for a way to change this behavior so that literal strings would get skipped and found shopt -s nullglob
. My understanding was that this theoretically should be equivalent to the way tcsh behaves, but today I discovered a difference. When I do ls ../*.doesnotmatch
, the result was a list of the current directory's contents. Specifically, I did this:
bash:
$ shopt -s nullglob
$ ls ../*.sam
extractSplitReads_BwaMem extractSplitReads_BwaMem.xml
$ shopt -u nullglob
$ ls ../*.sam
ls: ../*.sam: No such file or directory
There's nothing in the parent directory that matches *.sam
, particularly not the current directory. I was really confused at first, but then I realized that the glob pattern is disappearing and that the command was executing as if I had not supplied any arguments, e.g.:
$ ls
So I tried setting failglob both by itself and with nullglob, but as long as failglob is set, any non-matching glob pattern kills the command, whether or not a matching pattern is present:
bash:
$ shopt -s failglob
$ shopt -s nullglob
$ ls ../vis*/*.xml
../visualization/LAJ.xml
$ ls ../vis*/*.xml ../*.sam
bash: no match: ../*.sam
$ ls ../{vis*/*.xml,*.sam}
bash: no match: ../*.sam
When I am using tcsh, all globs are boiled down to just those things that matched and if nothing matches, you get a glob error:
tcsh:
$ ls ../vis*/*.xml ../*.sam
../visualization/LAJ.xml
$ ls ../{vis*/*.xml,*.sam}
../visualization/LAJ.xml
$ ls ../*.sam
ls: No match.
I looked through the shopt settings, but I don't see a way to get this behavior. Am I missing something? Is there another modern shell besides bash or tcsh that treats globs the way tcsh does? I want the behavior of nullglob when something matches, but the behavior of failglob when nothing matches, which seems to be how tcsh works.
zsh
instead ofbash
would be a much more natural transition.zsh
globs work likebash -O failglob
by default, but you can use thecshnullglob
option for it to behave liketcsh
. See also Why is nullglob not default?