0

I wanted to execute several statements in an if condition as follows:

[[ -e cli.tar.gz ]] && `tar -xvf cli.tar.gz -C ~/` || 
        echo "cli.tar.gz can not be found. Exiting" >&2 && exit 1

What happened is that the exit 1 was reached when cli.tar.gz is found and tarred, i.e. exactly the opposite of what I was trying to achieve, so I added parentheses around the part after the OR as follows

[[ -e cli.tar.gz ]] && tar -xvf cli.tar.gz -C ~/ || 
   { echo "cli.tar.gz can not be found. Exiting" >&2; exit 1; }

I received an error unexpected token '}' at the end of the function (this line is part of a function)

I solved my problem by writing a good formatted if condition like this

if [[ -e cli.tar.gz ]]
then
    if [[ ! `tar -xvf cli.tar.gz -C ${UDM_REMOTE}` ]]
    then
        echo "cli.tar.gz can not be found. Exiting" >&2; exit 1;
    fi
fi

But still what would have been the solution of doing nested if condition in one line like what I've tried to achieve initially?

Update 1 Thanks to the answers of @uwe and @goldilocks I updated the statement as follows which now works

[[ ! -e cli.tar.gz ]] && ! `tar -xvf cli.tar.gz -C ~/` && \ 
  echo "cli.tar.gz can not be found (or tar failed). Exiting" >&2 && exit 1
3
  • The backqotes in your last version don't make sense. With the backquotes, you would take the standard output of tar ...; execute it, and then consider the exit status of that execution. You don't want that.
    – Uwe
    Nov 13, 2013 at 12:31
  • @Uwe actually that's exactly what I want, if the tar does not exist or cannot be untarred then echo error and exit 1. Nov 14, 2013 at 13:11
  • No, it's not. When bash finds a command in backquotes, it executes the command, replaces it by its standard output, and continues execution. For instance, if you put echo ls in backquotes, then the echo command yields the output ls, and the ls command is then executed. In your example, bash executes the tar command, takes its standard output (i.e., a list of file names), and interprets that as a command to execute - but in general, that's not a reasonable command to execute. You could assign it to a variable, i.e., var=`tar ...` , but omitting the backquotes makes more sense.
    – Uwe
    Nov 14, 2013 at 22:19

3 Answers 3

4
if [[ ! -e cli.tar.gz ]] || ! tar -xvf cli.tar.gz -C ~/ ; then 
    echo "cli.tar.gz can not be found (or tar failed). Exiting" >&2 && exit 1
fi

or, if you want to avoid the if completely:

( [[ ! -e cli.tar.gz ]] || ! tar -xvf cli.tar.gz -C ~/ ) && { echo "cli.tar.gz can not be found (or tar failed). Exiting" >&2 ; exit 1 ; }
4
  • +1 Negating the 1st arg makes more sense than not.
    – goldilocks
    Nov 13, 2013 at 11:16
  • 1
    Works for me, even if I put it into a shell function. Could you show the entire part of the script that fails?
    – Uwe
    Nov 13, 2013 at 12:27
  • Sheepish grin -- I left ; exit 1; out because it closed the GUI terminal outside of a script. So then there was no closing ;. All apologies! Some unconscious js- or perl- ishness musta made me figure a closing semi-colon was irrelevant...bash often bites me, lol. Will up my vigilance.
    – goldilocks
    Nov 13, 2013 at 14:23
  • @goldilocks, yah happens to me all the time, replace it with another echo though and see if u reach the 2nd echo Nov 14, 2013 at 13:13
4
if [[ -e cli.tar.gz && ( $(tar -xvf cli.tar.gz -C ~/) && $? != 0 ) ]]; then 
    echo "cli.tar.gz can not be found (or tar failed). Exiting" >&2 && exit 1
fi
3
  • That doesn't work (for several reasons: tar ... is not a conditional expression, and the logic is wrong).
    – Uwe
    Nov 13, 2013 at 10:58
  • 1
    yah but tar can be surrounded by `` and it will return either 0 or non 0, and so it can be used as a conditional expression Nov 13, 2013 at 11:14
  • @Uwe You got me...corrected.
    – goldilocks
    Nov 13, 2013 at 11:15
2

The manner in which you're using && and || is as a list, not a conditional.

Lists
   A list is a sequence of one or more pipelines separated by one of the 
   operators ;, &, &&, or ⎪⎪, and optionally terminated by one of ;, &, or 
   <newline>.

   Of these list operators, && and ⎪⎪ have equal precedence, followed by ; 
   and &, which have equal precedence.

   A sequence of one or more newlines may appear in a list instead of a 
   semicolon to delimit commands.

   If a command is terminated by the control operator &, the shell executes 
   the command in the background in a subshell.  The shell does not wait for 
   the command  to  finish,  and the  return  status  is 0.  Commands 
   separated by a ; are executed sequentially; the shell waits for each 
   command to terminate in turn.  The return status is the exit status of
   the last command executed.

   AND and OR lists are sequences of one of more pipelines separated by the 
   && and ⎪⎪ control operators, respectively.  AND and OR lists are executed 
   with left associativity.   An AND list has the form

          command1 && command2

   command2 is executed if, and only if, command1 returns an exit status of 
   zero.

   An OR list has the form

          command1 ⎪⎪ command2

   command2 is executed if and only if command1 returns a non-zero exit 
   status.  The return status of AND and OR lists is the exit status of the 
   last command executed in the list.

If you take a look at this SO Q&A titled: Simple logical operators in BASH, @Gilles answer is as about as concise as it gets in terms of explaining how to contend with if/then blocks in Bash.

Re-factored run #1

So a re-factored version of your if/then statement:

[[ -e cli.tar.gz && `tar -xvf cli.tar.gz -C ~/` || 
        echo "cli.tar.gz can not be found. Exiting" >&2 && exit 1

would look like this:

if [[ -e cli.tar.gz && ! $(tar xvf cli.tar.gz -C out) ]]; then echo "cli.tar.gz can not be found. Exiting" >&2; exit 1; fi

Or expanded out for readability:

if [[ -e cli.tar.gz && ! $(tar xvf cli.tar.gz -C out) ]]; then 
  echo "cli.tar.gz can not be found. Exiting" >&2
  exit 1
fi

Example run

Sample data. Contents of tarball:

$ tar ztvf cli.tar.gz 
drwxrwxr-x saml/saml         0 2013-11-13 08:26 1/
drwxrwxr-x saml/saml         0 2013-11-13 08:26 1/2/
drwxrwxr-x saml/saml         0 2013-11-13 08:26 1/2/3/
drwxrwxr-x saml/saml         0 2013-11-13 08:26 1/2/3/4/

File info:

$ ls -l cli.tar.gz 
-rw-rw-r-- 1 saml saml 146 Nov 13 08:27 cli.tar.gz

$ ls out/
1

Running our command:

$ if [[ -e cli.tar.gz && ! $(tar xvf cli.tar.gz -C out) ]]; then echo "cli.tar.gz can not be found. Exiting" >&2; exit 1; fi

Nothing happened. If we remove the output dir. out:

$ mv out out_ORIG

And re-run:

$ if [[ -e cli.tar.gz && ! $(tar xvf cli.tar.gz -C out) ]]; then echo "cli.tar.gz can not be found. Exiting" >&2; fi
tar: out: Cannot chdir: No such file or directory
tar: Error is not recoverable: exiting now
cli.tar.gz can not be found. Exiting

What happened? For one you generally do not want to mix the running of commands in a if/then as you're attempting to do. There are too many issues that can go awry.

Re-factored run #2

Rather I'd structure my code like this, since it gives us the easiest path for dealing with issues as they pop up, rather than try and construct some ridiculous code which even you the author will struggle in 6 months to understand what you were doing!

if [[ -e cli.tar.gz ]]; then 
  cmdOutput=$(tar xvf cli.tar.gz -C out 2>&1); 
  if [[ $? != 0 ]]; then 
    echo "cli.tar.gz can not be found. Exiting" >&2
    exit 1
  fi
fi

Example

$ ls -l |grep -E "cli|out"
-rw-rw-r-- 1 saml saml  146 Nov 13 08:27 cli.tar.gz
drwxrwxr-x 3 saml saml 4096 Nov 13 09:28 out

Now run our command (nothing happens):

$ if [[ -e cli.tar.gz ]]; then cmdOutput=$(tar xvf cli.tar.gz -C out 2>&1); if [[ $? != 0 ]]; then echo "cli.tar.gz can not be found. Exiting" >&2; exit 1;fi;fi
$ 

Now run our command (without dir. out present):

$ rm -fr out
$ ls -l |grep -E "cli|out"
-rw-rw-r-- 1 saml saml  146 Nov 13 08:27 cli.tar.gz

$ if [[ -e cli.tar.gz ]]; then cmdOutput=$(tar xvf cli.tar.gz -C out 2>&1); if [[ $? != 0 ]]; then echo "cli.tar.gz can not be found. Exiting" >&2; exit 1;fi;fi
cli.tar.gz can not be found. Exiting

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.