Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I apologize in advance if this is a duplicate question. I did make an effort to search/check before asking here.

I'm comfortable with writing one-liners like this:

foocommand && foocommand2 && foocommand3

The idea being that I only want subsequent commands to run if the previous one was "successful".

I'm writing a somewhat lengthy script and this one-liner isn't feasible because it looks like a huge block of confusing code to everyone else.

I want to space out the commands and write comments inbetween them in the script. How can I do this and still have the equivalent of && in there?

share|improve this question
4  
This bears repeating: && doesn't mean the subsequent command will run if the previous one was successful. It means the command will run if the collective result of all the previous commands in the command list is success. You may know this, but future readers may misunderstand. –  kojiro Feb 3 '13 at 0:41
1  
Also just as a note, this behavior you're describing that comes from ||and && (as opposed to | or &) is called short circuiting. The behavior used with the latter operators is called eager evaluation. –  AndyPerfect Feb 3 '13 at 2:03
4  
@kojiro: I don't see the distinction. a && b && c will only run b if a succeeds, so "it only runs c if b succeeds" and "it only runs c if a and b both succeed" are equivalent statements: b can't succeed unless a succeeded. –  ruakh Feb 3 '13 at 6:13
1  
@ruakh a || b && c is more illustrative. –  kojiro Feb 3 '13 at 11:49
5  
@ruakh no, true || false && echo hi will output hi. The specification reads, The operators "&&" and "||" shall have equal precedence and shall be evaluated with left associativity. –  kojiro Feb 3 '13 at 22:31

7 Answers 7

up vote 23 down vote accepted

You can do it like this:

#!/bin/sh
ls -lh &&
# This is a comment
echo 'Wicked, it works!'

I hope I understood what you asked correctly.

share|improve this answer
    
Thanks. This is the closest to what I was hoping to do. And it makes it very easy to convert back into a one liner if needed. –  Mike B Feb 2 '13 at 21:18
    
Mike, since you're concerned about code cleanliness, a common convention is to indent the 2-N sections of the chain underneath the first. –  jblaine Feb 13 '13 at 19:42
    
@jblaine I'm not sure I understand what you mean. Can you please elaborate? –  Mike B Feb 16 '13 at 19:41
    
Mike, Stackexchange won't let me format my answer properly, so here is all I meant: indent –  jblaine Apr 3 '13 at 19:03

You can change the shebang line to

#!/bin/bash -e

After any error, the script will stop.

share|improve this answer
1  
Interesting. I'll keep that in mind. Thank you. –  Mike B Feb 2 '13 at 21:19
    
saves me from typing set -e when I need it –  Silverrocker Feb 2 '13 at 21:38
    
@Silverrocker It is POSIX too (except for the bash part of course). The POSIX shell takes a bunch of options that are applicable to set. Or vice versa? set is a way of setting shell command line options within the script. –  Kaz Feb 3 '13 at 3:38
6  
Unfortunately, there are a number of standard utilities that don't follow the usual exit-status conventions, so -e can misbehave. For example, you probably don't want your script to terminate every time you diff two non-identical files. You can, of course, work around this by appending || true (or perhaps || [ $? = 1 ]) to such commands, but the OP mentions an "everyone else" who will have to read this script, and said "everyone else" will probably not be used to having to cope with -e. –  ruakh Feb 3 '13 at 6:23
    
@ruakh: I find the exit status of let x++ for x=0 even worse. But I still use it sometimes. It really depends on what commands you need. –  choroba Feb 3 '13 at 17:12

If you don't like the set -e idea, maybe you can invert the logic.

foocommand || exit 1
foocommand2 || exit 2
foocommand3 || exit 3

More usefully, replace exit with something to print a useful error message, then exit. Inside a function, of course, you want return instead of exit.

share|improve this answer
7  
Or foocommand || exit to exit with foocommand's exit status if non-zero. –  Stéphane Chazelas Feb 2 '13 at 21:10
    
Awesome info, thanks. –  Mike B Feb 2 '13 at 21:19
    
How does this work? Is it like in C - if the left argument evaluates to TRUE, the no more arguments are checked? –  Vorac Mar 29 '13 at 15:39
    
Yes, that's right. It's a common idiom in Lisp (and functional languages in general, I suppose) and in Perl. –  tripleee Mar 30 '13 at 8:14

You can use if else fi blocks instead.

if foocommand; then

  # some comments

  if foocommand2; then

    # more comments

    foocommand3
  fi
fi

It's a little more readable.

Alternatively you can just use \ to break your big 1 liner into several lines

foocommand && \
  # some comment
  foocommand2 && \
    # more comment
    foocommand3

But of course this can be confusing to the untrained eye.

share|improve this answer
    
Thank you very much. –  Mike B Feb 2 '13 at 21:18
2  
I don't think the \ is necessary there. –  Samuel Edwin Ward Feb 3 '13 at 1:49
    
You are right. Force of habit. –  Martín Canaval Feb 3 '13 at 2:15

You could consider using nested if statements. Other option is to use curly's to group like { true && echo hi; } || echo huh

Update 1:

Here's an example without newlines/comments:

{ { false && echo "inner true"; } && { echo "inner true" && true; } || { echo "inner false" && false; } || echo "outter false"; }
share|improve this answer
    
@Stephane Thanks for adding semicolon. I've been using my phone to reply so I haven't been double checking my answers. :) –  livingstaccato Feb 2 '13 at 21:52
    
Interesting idea. Yeah, I had thought of nested if statements but if I'm chaining a lot of stuff together it might get messy. –  Mike B Feb 2 '13 at 22:38

Split each sequence of commands into functions:

big_block_1() {
  # ...
}
big_block_2() {
  # ...
}
big_block_3() {
  # ...
}

big_block_1 && big_block_2 && big_block_3
share|improve this answer

I always like to write a "fail" function which lets me specify the thing I was going to do when the failure occurred and then use the || pattern. Like the following:

function fail() {
  echo "Failure: ${@}"
  exit 1
}

foocommand || fail "couldn't foo"
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.