Faking (or not) a ternary if operator with && and ||

April 11, 2012

Suppose that you have a multiline if in some language and you want to rewrite this into a single line and a single expression; however, your language only has && and || operators without a ternary if operator (this is C's ?: operator). Can you do this rewrite safely?

To be concrete, let's do this in the Bourne shell (which is where I saw this be done recently). Suppose you have the following multiline bit of shell script and you want to reduce it to something that's as short as possible:

if cmd1; then
  cmd2
else
  cmd3
fi

Is a good translation of this 'cmd1 && cmd2 || cmd3'?

(Let's ignore the set -e gotcha.)

The answer is no. It's easy to see why this is if I rewrite this with specific commands:

true && false || echo oops

This will echo 'oops'.

The answer to the question turns out to depend on whether you care about the value of the ternary if (or the multiline if). If you don't care about the value and are evaluating things purely for their side effects, then you can write 'a ? b : c' as 'a && (b || true) || c', which avoids the problem by forcing the middle clause to always be considered true. If you do need to expose the (truth) value of b, this rewrite is no good and you are almost certainly up the creek; the only way out is if you know that b's value will always considered true.

(I first saw this problem in Python, but seeing it reappear in the Bourne shell started me down the path of thinking about the general issue.)

The Bourne shell version of this rewrite would be

cmd1 && (cmd2 || true) || cmd3

I think that most of the time this should be fine; it would be very odd style to check the exit code of cmd2 or cmd3 after the if, and certainly the example I saw this in didn't do it. My one twitch is that I am reflexively nervous about () potentially introducing surprise subshells, but if cmd2 is a real command that's irrelevant.


Comments on this page:

From 109.76.57.44 at 2012-04-11 04:38:18:

This would save a subshell on dash at least:

cmd1 && { cmd2; :; } || cmd3

Written on 11 April 2012.
« Revisiting checksum functions
Hypervisors are not microkernels »

Page tools: View Source, View Normal, Add Comment.
Search:
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Wed Apr 11 01:15:38 2012
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.