0

Say that I want to make a script that extracts all tar.gz and tar.bz2 files that are listed as arguments. What I've done so far is something like:

if [[ $@ =~ .*"tar.gz".* ]] || [[ $@ =~ .*"tar.bz2".* ]]; then
 for i in $@ =~ .*"tar.gz".*; do
  tar -xvzf "$i"
 done
 for p in $@ =~ .*"tar.bz2".*; do
  tar -xvjf "$p"
 done
else
 echo "tar.gz or tar.bz2 files required."
fi 

The first line is successful at evaluating whether or not a tar.gz or tar.bz2 file exist, but my problem is the rest. The variables aren't set properly, and the script ends up trying to extract each variable listed with both extraction commands. How can I separate arguments that end with tar.gz and tar.bz2 to perform separate extractions?

2
  • 1
    FYI, [[ $* =~ ... ]] (running a regex comparison against a string created by combining all arguments) is much clearer in terms of its behavior than [[ $@ =~ ... ]] (trying to run a regex against an array; there's no documented guarantees in terms of what behavior will be in this case, or that it will continue to work in the future at all). Commented May 8, 2017 at 17:37
  • ...that said, for i in $@ =~ ...; do just isn't defined behavior at all. Commented May 8, 2017 at 17:37

3 Answers 3

3

You should probably iterate over arguments and test each argument, like this :

#!/bin/bash

declare -i count
for file in "$@"
do
  if
    [[ $file =~ .*[.]tar[.]gz$ ]]
  then
    tar -xvzf "$file"
  elif
    [[ $file =~ .*[.]tar[.]bz2$ ]]
  then
    tar -xvjf "$file"
  else
    continue
  fi
  count+=1
fi 

((count)) || echo "tar.gz or tar.bz2 files required."

If you are OK to rely on GNU tar choosing the right decoding algorithm automatically, you can simplify this to :

#!/bin/bash

declare -i count
for file in "$@"
do
  [[ $file =~ .*[.]tar[.](gz|bz2)$ ]] || continue
  tar -xvf "$file"
  count+=1
fi 

((count)) || echo "tar.gz or tar.bz2 files required."
Sign up to request clarification or add additional context in comments.

10 Comments

Need to decide between z and j based on the type.
Oops, missed that. Thanks.
(Personally, btw, I'm not a fan of declare -i, as it's a construct that creates context that has to be known to correctly grok the behavior of code elsewhere in the script -- one can easily read count+=1 to be a bug that would create count=11 rather than count=2 in its absence).
@CharlesDuffy I understand the failure mode you are describing, but is it really worse than using, say, count=$((count+1)) and having count contain a string that would them be converted to an unforeseen integer value other than 0? What would you suggest as a replacement?
Pattern matching would be simpler than regex matching here: [[ $file = *.tar.gz ]], etc. The last example (assuming extended pattern matching is enabled) would be [[ $file = *.tar.@(gz|bz2) ]].
|
3

There isn't any built-in syntax for extracting and iterating over only matching elements of an array, as you're trying to do here.

The below is POSIX-compliant, depending on no bashisms:

count=0
for arg; do
  case $arg in
    *.tar.gz)  tar -xvzf "$arg"; count=$(( count + 1 ));;
    *.tar.bz2) tar -xjvf "$arg"; count=$(( count + 1 ));;
  esac
done
if [ "$count" -eq 0 ]; then
  echo "tar.gz or tar.bz2 files required" >&2
fi

2 Comments

perhaps worth mentioning, for educational purposes, that under bash you can say ((++count))
@ccarton, indeed, I did so in a prior version (apparently within the edit window since I don't see the history link), until deciding to distinguish this answer by making it POSIX-compliant (at which point I also switched from (( count == 0 )) to [ "$count" -eq 0]).
0

A bit off-topic, when extracting with GNU tar it detects the archive type automatically from the file extension since 2007, no need to specify z|j|J.

I.e. tar xvf <filename>.

4 Comments

You mean z|j. And there's no standard for tar (the POSIX-standardized archiver with support for tar-formatted archives is pax), so what you're describing here necessarily applies to a very specific implementation, and probably a specific range of versions within that implementation.
@CharlesDuffy I am describing portable GNU tar, all other tar's are non-portable.
"All other"? Nonsense. Busybox has a quite portable tar, and I'm almost certain I've seen a ports-style distribution with a BSD tar packaged for multi-OS use, though I'd need to look around to find it.
@CharlesDuffy Busybox also auto-detects the archive type from the file magic, so it seems to be portable as well :)))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.