Sign up ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

I've got a directory that looks like

$ ls
Broad_hapmap3_r2_Affy6_cels_excluded.tgz  DINGO.tgz                     GIGAS.tgz  index.html          IONIC.tgz             passing_cels_sample_map.txt  SCALE.tgz
CHEAP.tgz                                 EPODE.tgz                     HOMOS.tgz  index.html?C=M;O=A  LOVED.tgz             PICUL.tgz                    SHELF.tgz
CORER.tgz                                 excluded_cels_md5.txt         HUFFS.tgz  index.html?C=N;O=D  NIGHS.tgz             POSIT.tgz                    SLOTH.tgz
CUPID.tgz                                 excluded_cels_sample_map.txt  HUSKS.tgz  index.html?C=S;O=A  passing_cels_md5.txt  SAKES.tgz                    TESLA.tgz

I want to unzip all the files that match the extension *.tgz with a single command, except Broad_hapmap3_r2_Affy6_cels_excluded.tgz.

I can do

ls *.tgz | xargs -n1 tar zxvf

for all the *tgz files, but what's a good way to exclude a subset of them? From reading online maybe find is indicated in this situation, but it seems like overkill. Thank in advance.

Addendum: I'd also be interested in alternative methods to the question without excluding files.

share|improve this question
Thanks to everyone for their answers. I'm sorry that I can only accept one. :-) – Faheem Mitha Feb 16 '11 at 18:16

3 Answers 3

up vote 0 down vote accepted

You could always do:

ls *.tgz | grep -v Broad_hapmap3_r2_Affy6_cels_excluded.tgz | xargs -n1 tar zxvf

But I suspect somone will post a cleaner way do do this directly from the bash shell without needing a grep in there.

share|improve this answer
That's a nice solution. Thanks. – Faheem Mitha Feb 16 '11 at 17:05
In general, you want to avoid parsing the output of ls when possible. – Steven D Feb 16 '11 at 19:11
@Steven: Sorry, don't follow. Can you elaborate? – Faheem Mitha Feb 16 '11 at 19:20
When you parse the output of ls you can run into a lot of problems if you encounter filenames with special characters. See the following for a better explanation: – Steven D Feb 16 '11 at 19:27
@Steven: Thanks for the link. The referenced article mentions newlines, but now that I think about it, in Windows world for example, they like filenames with spaces in them, and try to export it elsewhere, and that would by itself mess up the oneliner here, I think. What would you prefer as an alternative? find? – Faheem Mitha Feb 16 '11 at 19:32

In zsh:

setopt extended_glob
for z in *.tgz~Broad_hapmap3_r2_Affy6_cels_excluded.tgz; do tar xzf $z; done

Another possibility in zsh:

setopt extended_glob
zargs -n 1 -- *.tgz~Broad_hapmap3_r2_Affy6_cels_excluded.tgz -- tar xzf

In ksh (but not bash or zsh as they don't support and patterns):

for z in @(*.tgz&!(Broad_hapmap3_r2_Affy6_cels_excluded.tgz); do tar xzf "$z"; done

Another way in ksh:

( FIGNORE=Broad_hapmap3_r2_Affy6_cels_excluded.tgz;
  for z in *.tgz; do tar xzf "$z"; done )

In bash:

( GLOB_IGNORE=Broad_hapmap3_r2_Affy6_cels_excluded.tgz;
  for z in *.tgz; do tar xzf "$z"; done )

In any shell:

for z in *.tgz; do
  [ "$z" = Broad_hapmap3_r2_Affy6_cels_excluded.tgz ] || tar xzf "$z"

Yet another hackish way:

mv Broad_hapmap3_r2_Affy6_cels_excluded.tgz Broad_hapmap3_r2_Affy6_cels_excluded.tgz.not
for z in *.tgz; do tar xzf "$z"; done
mv Broad_hapmap3_r2_Affy6_cels_excluded.tgz.not Broad_hapmap3_r2_Affy6_cels_excluded.tgz

If you also want to act on files in subdirectories, find is the natural tool to turn to. The recursive examples below exclude files named Broad_hapmap3_r2_Affy6_cels_excluded.tgz in any subdirectory.

find . -name '*.tgz' -type f \
       \! -name Broad_hapmap3_r2_Affy6_cels_excluded.tgz \
       -exec tar xzf {} \;

For a recursive traversal in zsh or bash ≥4, you can use ** in patterns. In zsh:

setopt extended_glob
zargs -n 1 -- **/*.tgz~**/Broad_hapmap3_r2_Affy6_cels_excluded.tgz -- tar xzf

In bash ≥4:

shopt -s globstar
( GLOB_IGNORE=Broad_hapmap3_r2_Affy6_cels_excluded.tgz;
  for z in **/*.tgz; do tar xzf "$z"; done )

You can even use find if you don't want a recursive traversal, though it's not the most convenient way then.

find *.tgz  -type f \
            \! -name Broad_hapmap3_r2_Affy6_cels_excluded.tgz \
            -exec tar xzf {} \;
share|improve this answer
I think you may have reversed the order of the filenames in your last example. That, or I'm missing something obvious. – Steven D Feb 16 '11 at 19:52
@Gilles: Thanks, that's quite a variety of approaches! But you forgot to include an approach using find. :-) – Faheem Mitha Feb 16 '11 at 19:58
@Steven: fixed. @Faheem: done. – Gilles Feb 16 '11 at 20:11
@Gilles: thanks. – Faheem Mitha Feb 16 '11 at 20:45
@Gilles: fyi, if you put two @user in the same comment, only the first is notified. See item 7 in… – Faheem Mitha Feb 16 '11 at 21:29

For one-off things like this, I'd just brute-force it:

for f in *.tgz; do 
   [[ "$f" != "Broad_hapmap3_r2_Affy6_cels_excluded.tgz" ]] && tar zxvf "$f"
share|improve this answer

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.