1

So I'm now doing quite a bit more bash programming in the last week that I thought I'd do.

I've seen code that uses [ condition ] && { ... } and if [ condition ]; then ...; fi type constructs.

As far as I understand [ condition ] && { ... } is equivalent to if [ condition ]; then ...; fi.

However with the former construct you can't do an else.

Can someone please explain why you'd use one over the other, the advantages/disadvantages and what would be best practice here.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
hookenz
  • 36,432
  • 45
  • 177
  • 286
  • 1
    There are existing, answered questions comparing `[ ]` and `[[ ]]`. Removing that subquestion would reduce the scope enough to make this easier to follow and answer. – Charles Duffy Jun 10 '15 at 20:07
  • 1
    ...by the way, you might find BashFAQ #31 interesting: http://mywiki.wooledge.org/BashFAQ/031 – Charles Duffy Jun 10 '15 at 20:09
  • ...moreover, https://stackoverflow.com/questions/3953645/ternary-operator-in-bash is moderately close to a duplicate of the other part of the question, but be very sure that you read the comments and caveats, as the commonly-given answer leads to bugs when misapplied (for the same reason that Python added a ternary one-liner rather than letting folks continue to abuse short-circuiting boolean logic operators for flow control). – Charles Duffy Jun 10 '15 at 20:11
  • Thanks, I didn't find any other questions... they normally do pop up as suggestions but didn't in my browser for some reason. :) – hookenz Jun 10 '15 at 20:12
  • https://stackoverflow.com/questions/669452/is-preferable-over-in-bash-scripts is one (on the `[ ]` vs `[[ ]]` thing). – Charles Duffy Jun 10 '15 at 20:12
  • Thanks for the corrections @CharlesDuffy – hookenz Jun 10 '15 at 20:23
  • @CharlesDuffy: "`cond` is a bash keyword"? Since when? – rici Jun 10 '15 at 21:01
  • Damn. I've had my LISP hat on today, and... *red face*. – Charles Duffy Jun 10 '15 at 21:02
  • Note that `&&` and `||` are *not* the Boolean operators you are familiar with, although very similar. The big difference is that they have equal precedence, so that `c1 || c2 && c3` is parsed the same as `{ c1 || c2; } && c3`, not `c1 || { c2 && c3; }`. This makes a big difference, e.g. if `c1` succeeds and you expect neither `c2` nor `c3` to run. Instead, `c3` runs if *either* `c1` or `c2` succeed. – chepner Jun 10 '15 at 22:34

1 Answers1

4

[[ ]] is built-in conditional syntax in ksh, adopted by bash, zsh and others. Think of it as a more powerful version of the [ command (which is a different name for the command also called test -- and to be clear, [ is a command, not syntax). It is not a flow control operator, and thus largely off-topic in a discussion about flow control.


some-command && other-command is short-circuiting boolean logic. That is to say, it's a statement that returns whether both commands return truthful values.

If and only if some-command succeeds, it is necessary to run other-command to know whether both commands succeed. Thus, as a side effect of this logic operator, it happens to behave in a manner similar to an if statement.

Similarly, some-command || other-command returns a value which indicates whether one of the commands or the other has succeeded. In order to determine if either of the two is true, it is only necessary to run the one on the right if the the one on the left fails; if the one on the left succeeds, then it is already known a priori that one of the two has succeeded.

This behavior, and the associated idioms, are common to all languages implementing short-circuiting boolean logic with a well-defined order of operations; no part of this is specific to bash.


So, why would you use the short-circuiting form? Terseness.

Why would you use the long form? Correctness, particularly in cases where one needs to combine operations. Consider:

foo && bar || baz

This looks much like:

if foo; then bar; else baz; fi

...however, it has a bug: If bar fails, then baz can be run even if foo succeeded. Thus, the if/then/else form is to be preferred unless one is very certain about desired behavior in the boolean-logic case.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • Thanks Charles, I do understand why that works. I'm just wondering why is it in some scripts a preferred syntax. It doesn't seem to offer any benefit except in some cases make for less writing and for those unfamiliar with bash harder to understand. – hookenz Jun 10 '15 at 20:18
  • 1
    "Less writing" is exactly the point (and the _only_ point; there are no other advantages), and I disagree with "harder to understand" in the simple case. Short-circuiting boolean logic is available in far, far more languages than there are ones that don't support it. Anyone who doesn't have it internalized risks being lost in far more languages than shell. – Charles Duffy Jun 10 '15 at 20:20
  • Looks like the third paragraph ought to read, "If `some-command` fails, it is *not* necessary to run...". – cHao Jun 10 '15 at 20:30
  • @Matt, *nod*. It also helps to stop thinking about `[ ]` or `[[ ]]` as being intrinsically tied to `if`; they're just commands, like any other. Being in the habit of using `[ ]` with each/every `if` is what leads folks to write `if [ "$(grep ...)" = "" ]` rather than the vastly more efficient `if grep -q ...; then ...`. – Charles Duffy Jun 10 '15 at 20:31
  • Also it seems that using [ ] form gets passed to test. Does that spawn the test program on some systems? that is less clear. – hookenz Jun 10 '15 at 20:35
  • @Matt, if your shell is bash, then the `[` command (like the `test` command) is always a builtin. Run `type [` and/or `type test` to see. – Charles Duffy Jun 10 '15 at 20:36
  • @Matt, ...and, again, `[` isn't a special form, it's just a command. There's a `/usr/bin/[` which will be used in shells that *don't* have a builtin for it; the builtin is just a performance optimization, performance is completely identical either way. – Charles Duffy Jun 10 '15 at 20:36
  • @Matt, ...which is why `[[` exists -- it *is* a special form, and so can play with the parser rules to add functionality that wouldn't otherwise be possible (such as regex and pattern matching with sensible quoting/escaping support) and turn off behavior that's useless or confusing (globbing, string-splitting, etc). – Charles Duffy Jun 10 '15 at 20:39
  • ...ugh, "just a performance optimization, performance is identical" should have been "just a performance optimization, behavior is identical". – Charles Duffy Nov 25 '19 at 15:56