Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I'm learning about decision making structures and I came across these codes:

if [ -f ./myfile ]
then
     cat ./myfile
else
     cat /home/user/myfile
fi


[ -f ./myfile ] &&
cat ./myfile ||
cat /home/user/myfile

Both of them behave the same. Are there any advantages to using one way from the other?

share|improve this question
    
The second version is not valid *sh code. – joepd 19 hours ago
    
    
@joepd Neither was the first. Corrected. – xhienne 18 hours ago
    
They are not equivalent. See the excellent answer by Icarus. For example, consider the case that ./myfile exists but is not readable. – AlexP 17 hours ago

Most people find it easier to comprehend the if ... then ... else ... fi form.

For the a && b || c, you have to be sure that b returns true. This is a cause of subtle bugs and is a good reason to avoid this style.

 $ if true; then false ; else echo boom ; fi
 $ true && false || echo boom
 boom

For very short tests and actions which don't have an else clause, the shortened length is attractive, e.g.

 die(){ echo -- "$0:" "$@" >&2 ; exit 1; }

 [ $# -eq 2] || die "Needs 2 arguments, input and output"

 if [ $# -ne 2 ] ; then
     die "Needs 2 arguments, input and output"
 fi
share|improve this answer

No, constructions if A; then B; else C; fi and A && B || C are not equivalent.

With if A; then B; else C; fi, command A is always evaluated and executed (at least an attempt to execute it is made) and then either command B or command C are evaluated and executed.

With A && B || C, it's the same for commands A and B but different for C: command C is evaluated and executed if either A fails or B fails.

In your example, suppose you chmod u-r ./myfile, then, despite [ -f ./myfile ] succeeds, you will cat /home/user/myfile

My advice: use A && B or A || B all you want, this remains easy to read and understand and there is no trap. But if you mean if...then...else... then use if A; then B; else C; fi.

share|improve this answer

Operator && executes the next command the if previous command had a successful execution, (returned exit code ($?) 0 = logical true).

In form A && B || C , command (or condition) A is evaluated and if A returns true (success, exit code 0) then command B is executed. If A fails (thus will return false - exit code other than 0) and/or B fails (returning false) then command C will be executed.

Also && operator is used as an AND in condition checks and operator || works like OR in condition checks.

Depending on what you want to do with your script, form A && B || C can be used for condition checks like your example or can be used to chain commands and ensure a series of commands to be executed if previous commands had a successful exit code 0.
This is why it is common to see commands like:
do_something && do_something_else_that_depended_on_something.

Examples:
apt-get update && apt-get upgrade If update fails then upgrade is not executed, (makes sense in the real world...).

mkdir test && echo "Something" > test/file
The part echo "Something" will be executed only if mkdir test was successful and operation returned exit code 0.

./configure --prefix=/usr && make && sudo make install
Usually found on compiling jobs to chain necessary dependent commands together.

If you try to implement above "chains" with if-then-else you will need much more commands and checks (and thus more code to write - more things to go wrong) for a simple task.

Also, keep in mind that chained commands with && and || are read by shell left to right. You might need to group commands and condition checks with brackets to depend the next step on the successful output of some previous commands . For example see this:

root@debian:$ true || true && false;echo $?
1 
#read from left to right
#true OR true=true AND false = false = exit code 1=not success

root@debian:$ true || (true && false);echo $?
0 
# true OR (true AND false)=true OR false = true = exit code 0 = success

Or a real life example:

root@debian:$ a=1;b=1;c=1;[[ $a -eq 1 ]] || [[ $b -eq 1 ]] && [[ $c -eq 2 ]];echo $?
1 
#condition $a = true OR condition b = true AND condition $c = false
#=> yields false as read from left to right, thus exit code=1 = not ok

root@debian:$ a=1;b=1;c=1;[[ $a -eq 1 ]] || [[ $b -eq 1 && $c -eq 2 ]];echo $?
0 
#vars b and c are checked in a group which returns false, 
#condition check of var a returns true, thus true OR false yields true = exit code 0

Keep in mind that some commands return different exit codes depending on the process executed, or return different codes depending on their actions, (for example command GNU diff, returns 1 if two files differ, and 0 if they don't). Such commands need to be treated with care in && and ||.

Also just to have all the puzzle together, mind the concatenation of commands using ; operator . With a format A;B;C all commands will be executed in series no matter what was the exit code of command A and B.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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