15

When I'm looking at bash script code, I sometimes see | and sometimes see ||, but I don't know which is preferable.

I'm trying to do something like ..

set -e;

ret=0 && { which ansible || ret=$?; }

if [[ ${ret} -ne 0 ]]; then
    # install ansible here
fi

Please advise which OR operator is preferred in this scenario.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
danday74
  • 52,471
  • 49
  • 232
  • 283
  • 5
    They mean totally different things. | is piping data. || is an or. – ceejayoz Jan 13 '17 at 00:31
  • What is `set e` supposed to do? – melpomene Jan 13 '17 at 00:50
  • https://www.gnu.org/software/bash/manual/bashref.html – melpomene Jan 13 '17 at 00:50
  • 2
    In this scenario, neither: `if ! which ansible; then ... fi`. – melpomene Jan 13 '17 at 00:51
  • `||` can also be used as a (true/false) string comparison operator... – l'L'l Jan 13 '17 at 00:51
  • most these comments would cause script failure ... if ! which ansible; then ... fi ... due to set e (which is why i explicitily added set e) ... script must not exit if ansible is installed or not – danday74 Jan 13 '17 at 00:54
  • set e causes script exit with non zero code if ANY script command exits with non zero code .. since i need to work with set e, i am seeking a way to prevent failure using OR – danday74 Jan 13 '17 at 00:55
  • No, `set e` sets `$1` to `e`. – melpomene Jan 13 '17 at 00:56
  • wrong see http://stackoverflow.com/questions/19622198/what-does-set-e-mean-in-a-bash-script – danday74 Jan 13 '17 at 00:56
  • That's `set -e`, not `set e`. – melpomene Jan 13 '17 at 00:57
  • fixed thx for pointing it out – danday74 Jan 13 '17 at 00:57
  • 1
    Did you actually try it? `if ! which ansible` doesn't cause script failure with `set -e`. – melpomene Jan 13 '17 at 00:58
  • crazy man, it works either way .. why does it work? ... if you add answer i will accept it ... ty – danday74 Jan 13 '17 at 01:03
  • 1
    @danday74, "why does it work"? Because `-e` causes an exit on **unchecked** failures. If you're branching on something, as with an `if`, then it's not unchecked. – Charles Duffy Jan 13 '17 at 01:04
  • 1
    @danday74, ...also, `set -e` is full of pitfalls -- unless you know bash well enough to know where they all are, it's perilous to use (and questionable even then). See [BashFAQ #105](http://mywiki.wooledge.org/BashFAQ/105) – Charles Duffy Jan 13 '17 at 01:05
  • Incidentally, `foo | bar` runs `foo` and `bar` *both at the same time*, with the output of `foo` connected to a FIFO which feeds to the input of `bar`. Because they're both running at the same time, it can't possibly run `bar` only if `foo` succeeds, because... well... they both got started at *the same time*, so obviously whether `foo` succeeded or failed isn't known yet when `bar` is started. (The exit status of that pipeline is the exit status of `bar` only, unless a non-default option such as `pipefail` is in use). – Charles Duffy Jan 13 '17 at 01:15
  • thx learnt a lot - updated my post with answer implemented (based on input from here) – danday74 Jan 13 '17 at 01:24
  • 2
    Don't destroy the question with your updates. You must preserve the substance of the original question, warts and all, so that answers continue to make sense. – Jonathan Leffler Jan 13 '17 at 01:26
  • As an aside -- editing answers into questions is actually something we frown on -- in means that there's an answer, unlike all the others, that can't be voted on independently of the question itself. If you have something significant to add that isn't covered in any answer, feel free/encouraged to add an answer yourself. See http://meta.stackoverflow.com/questions/267434/what-is-the-appropriate-action-when-the-answer-to-a-question-is-added-to-the-que – Charles Duffy Jan 13 '17 at 01:27
  • removed answer in question – danday74 Jan 13 '17 at 01:28

2 Answers2

18

| isn't an OR operator at all. You could use ||, though:

which ansible || {
  true # put your code to install ansible here
}

This is equivalent to an if:

if ! which ansible; then
  true # put your code to install ansible here
fi

By the way -- consider making a habit of using type (a shell builtin) rather than which (an external command). type is both faster and has a better understanding of shell behavior: If you have an ansible command that's provided by, say, a shell function invoking the real command, which won't know that it's there, but type will correctly detect it as available.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • Well, I'd say you're 100% in the context of the way it's being used as a string comparison operator, although the fake bash manual also states `||` it to be an `OR` operator. http://www.tldp.org/LDP/abs/html/ops.html – l'L'l Jan 13 '17 at 01:07
  • 2
    @l'L'l, ...don't quote the ABS at me. Find a *real* reference (the official manual, the bash-hackers wiki, etc). – Charles Duffy Jan 13 '17 at 01:08
  • That's invalid? I didn't know that. – l'L'l Jan 13 '17 at 01:08
  • 2
    Undermaintained and full of bad practices. – Charles Duffy Jan 13 '17 at 01:08
  • 2
    @l'L'l, that said, `||` is indeed an OR operator, and nothing in my answer states otherwise; it's `|` that isn't. – Charles Duffy Jan 13 '17 at 01:09
  • It's too bad that it comes up as the first thing most of the time when searching for bash references. So where's the "real" bash manual if you don't mind me asking. – l'L'l Jan 13 '17 at 01:09
  • 2
    @l'L'l, ...just realized I never answered your question. The *official* manual is at https://www.gnu.org/software/bash/manual/bash.html – Charles Duffy Jan 23 '20 at 17:54
  • »(...) consider making a habit of using type (a shell builtin) rather than which (an external command)« – Very good point. I just realized that `type` like `which` also has an `-a` option. There does not seem to be a builtin way though to just list the paths of the executables without the `curl is` or `curl is aliased to` prefix. This information may or may not be of interest, depending on the situation, but it would nice if there were some way to control the verbosity of the output. – Stefan Schmidt Oct 15 '22 at 09:36
  • 2
    @StefanSchmidt `command -v ls`; what's better is that it's required by POSIX, and thus portable across compliant shells. No `-a` there, though. – Charles Duffy Oct 15 '22 at 13:37
  • Certainly good to have a POSIX compliant option in the utility belt! – Stefan Schmidt Oct 15 '22 at 19:21
8

There is a big difference between using a single pipe (pipe output from one command to be used as input for the next command) and a process control OR (double pipe).

cat /etc/issue | less

This runs the cat command on the /etc/issue file, and instead of immediately sending the output to stdout it is piped to be the input for the less command. Yes, this isn't a great example, since you could instead simply do less /etc/issue - but at least you can see how it works

touch /etc/testing || echo Did not work

For this one, the touch command is run, or attempted to run. If it has a non-zero exit status, then the double pipe OR kicks in, and tries to execute the echo command. If the touch command worked, then whatever the other choice is (our echo command in this case) is never attempted...

Stefan Schmidt
  • 3,830
  • 1
  • 24
  • 20
ivanivan
  • 2,155
  • 2
  • 10
  • 11
  • 1
    It is common programming practice for a child process to return (exit with) zero to the parent signifying success. Source: [Wikipedia - Exit Status](https://en.wikipedia.org/wiki/Exit_status). So if it has a zero exit status, then the OR does not kick in, as a zero exit status means the first command was successful. – Sebastian Jun 28 '22 at 07:23
  • See also the section on OR lists in the [GNU Bash Reference Manual](https://www.gnu.org/software/bash/manual/bash.html#Lists) or in the [POSIX.1-2017](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_03_08) specification. – Stefan Schmidt Oct 14 '22 at 21:44