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
tar ...; execute it, and then consider the exit status of that execution. You don't want that.echo lsin backquotes, then theechocommand yields the outputls, and thelscommand is then executed. In your example, bash executes thetarcommand, 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.