[No relation to that infuriatingly viral video.]
A recent Reddit thread about ffmpeg encoding somehow sidetracked into an unrelated discussion about semicolons. In the process of answering the questions therein, I suddenly realized that most bash newbies get confused about semicolons in scripts, and that there’s in fact a striking similarity between compound statements in bash and C.
First, a simple fact: Semicolons punctuate compound statements, and punctuation promotes proper parsing. Just look at the descriptions of compound statements in the bash man page:
case word in [ [(] pattern [ | pattern ] ... ) list ;; ] ... esac
if list; then list; [ elif list; then list; ] ... [ else list; ] fi
for name [ [ in [ word ... ] ] ; ] do list ; done
and think about how any program could be expected to assume that:
for i in 1 2 3 do echo do you do this $i time\? done
actually means:
for i in 1 2 3; do echo do you do this $i time\?; done
as opposed to the programmer having a complete brain-fart.
Now, you’re probably going: “Hang on, I don’t see anyone writing code all in a line like that!” That leads me to my next point: Semicolons can generally be replaced by newlines in compound statements. Hence, you’re far more likely to see this:
for i in 1 2 3; do
echo $i
echo breaker
done
or this:
for i in 1 2 3
do
echo $i
echo breaker
done
Notice that the first form simply substitutes a newline for the semicolon before done
, and the second form also substitutes a newline for the semicolon before do
. Both are functionally identical to each other and to the single-line (note the newline-semicolon substitution between the two echo
statements as well):
for i in 1 2 3; do echo $i; echo breaker; done
as well as the odd duck:
for i in 1 2 3
do
echo $i
echo breaker; done
Don’t get crazy, though. The following is actually illegal in bash:
for i in 1
2
3
do
echo $i; echo
breaker; done
because it translates to the incorrect one-liner:
for i in 1; 2; 3; do echo $i; echo; breaker; done
“Wait a sec!” I hear you call, dear reader. “How about the double-semicolon in case
clauses?”
Technically, bash treats this double-semicolon as a single token, so you can’t replace it with two newlines (or one, for that matter). You also can’t write:
case $i in
(1) echo yes ; ;
esac
That space between the two semicolons will cause bash to choke.
Now, if you’re a C programmer, you probably recognized the “shape” of the first two examples as being the equivalent of the indentation styles1 popularly known as K&R:
while (1) {
do(something);
}
and Allman:
while (1)
{
do(something);
}
I’ll therefore call the equivalents in bash K&R-style and Allman-style. Which one to use is purely a matter of preference; pick one and stick with it.
As for the all-in-one-liner, I’ll just call it No-style. You should avoid this style wherever possible, as it gets seriously unreadable very quickly. You’ll also regret it when you get hit by a dozen syntax errors on line 1; good luck trying to find them all.
And if you were working for me, and showed me that odd-duck code chunk…let’s just say you’d no longer be working for me. Call it NoJob-style.
- See Indentation Style – Wikipedia for more styles than you should ever care about. ↩︎