Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

Is there any way in bash to glob files that does not end with a certain suffix ?

e.g. I'm doing this:


But I don't want to move the HDR_10_* that ends with .gz

share|improve this question
up vote 3 down vote accepted

This would work for the file move:

mv $( echo $INCDIR/HDR_10_* | grep -v '\.gz$') $BACKUPDIR

You also asked 'to glob files that does not end with a certain suffix ?' Bash has the shell option (shopt) extglob, which allows extended globbing syntax. The !(...glob...) matches everything but the glob pattern.

shopt -s extglob
mv !(*.gz) $BACKUPDIR

would work only if $INCDIR contained only files named HDR_10_*, specifically it would match any file or directory in $INCDIR that doesn't match *.gz. Technically you're asking for a glob that both matches one pattern but not another, which i don't think exists as a simple single entity.

share|improve this answer
Your first command simply won't work, as the echo command puts all names on one line. – Gilles Nov 12 '10 at 16:18
P.S. And if you try to fix that, you'll run into problems if a file name contains a special character such as whitespace or globbing characters. – Gilles Nov 12 '10 at 16:24
Since the patterns (the one to include and the one to exclude) don't overlap, it's actually easy to combine them: mv $INCDIR/HDR_10_!(*.gz) $BACKUPDIR. – Gordon Davisson Nov 12 '10 at 16:35

Under ksh, !(*.gz) matches all files in the current directory except the files that match *.gz. The same pattern works in bash after shopt -s extglob, and in zsh after setopt ksh_glob. Neither ksh nor bash has a way to take the intersection of a positive match and a negative match. In zsh, after setopt extended_glob , you can write $INCDIR/HDR_10_*~$INCDIR/*.gz.

One solution, in any Bourne-style shell (ash, bash, ksh, zsh, ...), is to iterate over the files and check each match.

for x in "$INCDIR"/HDR_10_*; do
  case "$x" in
    *.gz) :;;
    *) mv -- "$x" "$BACKUPDIR";;

Another solution is to use find. The following command will move files in subdirectories as well.

find "$INCDIR" -type f -name 'HDR_10_*' \! -name '*.gz' -exec mv {} "$BACKUPDIR" \;

If you have GNU find (e.g. under Linux), You can add -maxdepth 1 after "$INCDIR" to move files only directly in $INCDIR.

Note that You should always use double quotes around variable substitutions (e.g. "$INCDIR", not $INCDIR) unless you have a good reason to omit them. Otherwise you will run into trouble if you have files whose name contains special characters such as whitespace or \[?*.

share|improve this answer

If you use find, grep, and xargs, you can do this fairly easily.

I am sure there are better constructions, but this is one that comes to mind right away:

# find <here>| <pattern>    | <notpattern>| <what to do>
find $INCDIR | grep HDR_10_ | grep -v .gz | xargs mv $BACKUPDIR

A correction was pointed-out in the comments that the above quick-stab won't work because of mv not getting its arguments in the proper order from xargs:

find $INCDIR -name 'HDR_10_*' ! -name '*.gz' -exec mv {} $BACKUPDIR \
share|improve this answer
You can't use xargs here - xargs adds parameters to the end, you need after mv. Remember that find allows both positive and negative patterns as well. Maybe something like: find $INCDIR -name 'HDR_10_*' ! -name '*.gz' -exec mv {} $BACKUPDIR \; – Rich Homolka Nov 12 '10 at 15:59
Your command has many bugs. Beyond what Rich already said about xargs, the grep patterns are matching anywhere on the line, so they will include or exclude things that should not be (e.g. HDR_10_agz.txt will be excluded while NOT_HDR_10_blah will be included). It's possible to use grep here, but it's a lot simpler to use -name with find as per Rich's comment. Also your command will include files in subdirectories, which is not in the original specification. Then there is the generic problem with xargs requiring quoted output in a way that find can't produce. – Gilles Nov 12 '10 at 16:21
@Gilles - yes, the behavior as currently written will search anywhere on the line: that's not a "bug" in my mind, but a starting place that could take refinement based on the specific use. Didn't know about the xargs issue till @Rich Homolka said something though :) – warren Nov 12 '10 at 20:59
It's because of the issue with using xargs that (at least GNU) mv has --target-directory=DIRECTORY and find and xargs can use a null delimiter. So you can do xargs ... -print0 | xargs -0 --target-directory="$BACKUPDIR" and only get one copy of mv spawned (by not using -I with xargs). – Dennis Williamson Nov 13 '10 at 4:21

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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