0

I have many tar files like this: aa.tar, bb.tar, cc.tar, ....

I would like to untar them to their own directories such as aa/, bb/, cc/ ...

I tried this:

ls | xargs -n1 -I {} tar -xvf {} -C `basename {} .tar`

which does not work. It seems that the {} cannot be used as both whole tar file name and its file name without .tar.

How could I make it work?

3

You can't use {} in the command substitution (calling basename) as that is executed before even xargs is called. This means tar would try to change directory to a directory called {} (literally) for each archive.

Use a for loop, there's no need for xargs. e.g.

for t in *.tar ; do
  bn="$(basename "$t" .tar)"
  mkdir -p "$bn"
  tar -xvf "$t" -C "$bn"
done

See also: Why not parse ls (and what to do instead)?

0

The reason that your command does not work is that the command substitution (the bit you wrote with backticks) would be executed once, before xargs is even started.

Since you tagged this with xargs, I thought I'd give an xargs-based solution.

First, to unpack a single one of your tar archives, you could use

dir=${archive%.tar}
mkdir -- "$dir" && tar -x -f "$archive" -C "$dir"

... assuming that $archive contains the filename of the tar archive and that the archive is plain (not compressed).

You could obviously loop this with

for archive in *.tar; do
    dir=${archive%.tar}
    mkdir -- "$dir" && tar -x -f "$archive" -C "$dir"
done

but that would not let us use some of the commonly (albeit non-standard) features of xargs, such as running multiple commands in parallel.

To run our initial commands, four at a time, the names of the tar archives has to be fed into xargs on its standard input.

We can do that with

printf '%s\n' *.tar | xargs ...

This obviously assumes that no archive has a name that contains embedded newlines, but you could work around that by using

printf '%s\0' *.tar | xargs -0 ...

if your implementation of xargs supports the non-standard -0 option for reading nul-terminated arguments.

To run the unpacking in from xargs, it would be easiest to do in in a sh -c child shell:

printf '%s\0' *.tar |
xargs -0 -P 4 -I {} sh -c 'dir=${1%.tar}; mkdir -- "$dir" && tar -x -f "$1" -C "$dir"' sh {}

Within the sh -c shell script, $1 would be the same as $archive in our initial command, and the script would be started once for each archive. The -P 4 option to xargs would run four extractions in parallel, if your implementation of xargs supports the -P option.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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