Faking (or not) a ternary if operator with &&
and ||
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.
|
|