58

I was wondering whether (and, of course, how) it’s possible to tell tar to extract multiple files in a single run.

I’m an experienced Unix user for several years and of course I know that you can use for or find or things like that to call tar once for each archive you want to extract, but I couldn’t come up with a working command line that caused my tar to extract two .tar.gz files at once. (And no, there’s nothing wrong with for, I’m merely asking whether it’s possible to do without.)

I’m asking this question rather out of curiosity, maybe

  • there’s a strange fork of tar somewhere that supports this
  • someone knows how to use the -M parameter that tar suggested to me when I tried tar -zxv -f a.tgz -f b.tgz
  • we’re all blind and it’s totally easy to do — but I couldn’t find any hint in the web that didn’t utilize for or find or xargs or the like.

Please don’t reply with tar -zxvf *.tar.gz (because that does not work) and only reply with “doesn’t work” if you’re absolutely sure about it (and maybe have a good explanation why, too).

Edit: I was pointed to an answer to this question on Stack Overflow which says in great detail that it’s not possible without breaking current tar syntax, but I don’t think that’s true. Using tar -zxv -f a.tgz -f b.tgz or tar -zxv --all-args-are-archives *.tar.gz would break no existing syntax, imho.

scy
  • 820
  • Out of curiosity, what is the situation that caused you to start thinking about how to avoid a looping construct? – unclejamil Sep 01 '11 at 02:35
  • 1
    FYI: GNU tar supports multi-volume tar archives with the -M parameter. – Steve-o Sep 01 '11 at 02:49
  • 1
    unclejamil: I’m writing a HowTo and one of the steps in it involves unpacking several compressed files (.tar.bz2, .tar.gz, .zip). And since it’s a HowTo, I was wondering what the most elegant command line to do that would be. :) – scy Sep 01 '11 at 09:45

3 Answers3

64

This is possible, the syntax is pretty easy:

$ cat *.tar | tar -xvf - -i

The -i option ignores the EOF at the end of the tar archives, from the man page:

-i, --ignore-zeros
ignore blocks of zeros in archive (normally mean EOF)
polynomial
  • 2,461
  • 20
  • 15
  • 3
    This seemed to work for me. Exact command I used was: cat *.tar.bz2 | tar -ixjv. Thanks! – scy Sep 01 '11 at 09:40
  • 2
    cat .tgz | tar -xzvf - -i -C /destination/ for file in .tgz; do tar -xzvf $file -C /destination/; done – Ferroao Dec 06 '17 at 13:18
  • @polynomial: is there any reason, why we cant use ls instead of cat? wouldnt that work as well? – Hossein Feb 02 '20 at 03:55
  • 2
    @Rika, tar -f - reads from stdin, hence one needs to pipe the content of the archives, not their names. That's why ls wouldn't work. – Daniel Le Dec 29 '20 at 08:44
  • @DanielLe Thanks a lot really appreciate it – Hossein Dec 29 '20 at 09:23
  • to call this syntax easy is a stretch. tar -xf *.tar would be easy (and intuitive) – Baruch Jan 17 '21 at 07:35
  • This doesn't work on latest ubuntu: tar (child): -i: Cannot open: No such file or directory tar (child): Error is not recoverable: exiting now tar: Child returned status 2 tar: Error is not recoverable: exiting now – HamsterWithPitchfork Feb 22 '22 at 18:49
  • @HamsterWithPitchfork: Sounds to me like you’ve typed tar -xvf -i instead of tar -xvf - -i. That’s because -f requires a filename to follow. The special filename - means “standard input”, i.e. whatever is being piped into tar. If you omit that dash, tar will instead interpret the following -i as the file name. That file doesn’t exist, which is why it’s telling you -i: Cannot open: No such file or directory. – scy Dec 20 '22 at 13:26
  • And, by the way, -f - can probably be omitted altogether, because without -f, tar will read from standard input (or write to standard output, depending on the operation) anyway. So instead of tar -xvf - -i you could also write tar -xvif - and even tar -xvi. – scy Dec 20 '22 at 13:29
42

For *.tar.gz:

for file in *.tar.gz; do tar -zxf "$file"; done

For *.tar.bz2:

for file in *.tar.bz2; do tar -jxf "$file"; done

For *.tar.xz:

for file in *.tar.xz; do tar -Jxf "$file"; done

For *.tar.zst new:

for file in *.tar.zst; do tar --zstf -xf "$file"; done
Ratnesh
  • 437
  • this is great. the format is also great because thats how it looks in the terminal – smatthewenglish Apr 10 '15 at 11:31
  • 1
    Don't forget to put quotes around $file if your files have names with spaces. – Alexander Revo Dec 01 '16 at 13:22
  • 6
    This does not really answer the question of whether it's possible to extract multiple archives using a single invocation of tar. Furthermore, the user says they are already familiar with how to use for-loops to iterate over a set of files. – Kusalananda May 21 '18 at 12:35
2

You need to use a loop. It wouldn't break the tar command line syntax to allow multiple -f options, but it would require adding code to process several archives in sequence, with all kinds of edge conditions (What happens if an archive in the middle is malformed? Can the archives use different compression mechanisms? Can you have multiple -C options (GNU tar option to extract to a particular directory), too? What about -K (GNU tar option to start at a certain member name)? …).

One possibility avoiding a for loop is to install and activate AVFS, a FUSE filesystem that provides transparent access to archives. Each archive /path/to/archive doubles as a directory ~/.avfs/path/to/archive#. If you want to match the archives with wildcards, there's a hurdle of adding that # to a wildcard match; it can be done in zsh.

mountavfs
cp -p ~/.avfs$PWD/{a,b}.tgz\#/* /destination
cp -p ~/.avfs/path/to/source/*.tgz(e\''REPLY=$REPLY\#'\')/* /destination