Quoting and not quoting command substitution in the Bourne shell

October 21, 2024

Over on the Fediverse, I said something:

Bourne shell trivia of the day:
  var=$(program ...)
is the same as
  var="$(program ...)"
so the quotes are unnecessary.

But:
  program2 $(program ...)
is not the same as:
  program2 "$(program ..)"
and often the quotes are vital.

(I have been writing the variable assignment as var="$(...)" for ages without realizing that the quotes were unnecessary.)

This came about because I ran an old shell script through shellcheck, which recommended replacing its use of var=`...` with var=$(...), and then I got to wondering why shellcheck wasn't telling me to write the second as var="$(...)" for safety against multi-word expansions. The answer is of course that multi-word expansion doesn't happen in this context; even if the $(...) produces what would normally be multiple words of output, they're all assigned to 'var' as a single word.

On the one hand, this is what you want; there's almost no circumstance where you want a command that produces multiple words of output to have the first word assigned to 'var' and then the rest interpreted as a command and its arguments. On the other hand, the Bourne shell is generally not known for being friendly about its quoting. It would be perfectly in character for the Bourne shell to require you to quote the '$(...)' even in variable assignment.

On the one hand, shellcheck doesn't complain about the quoted version and it's consistent with quoting $(...) in other circumstances (when it really does matter). On the other hand, you can easily forget or not know (as I did) that the quoting is unnecessary here, and then you can be alarmed when you see an unquoted 'var=$(...)' in the wild or have it suggested. Since I've mostly written the quoted version, I'll probably continue doing so in my scripts unless I'm dealing with a script that already has some unquoted examples, where I should probably make everything unquoted so that no one reading the script in the future ever thinks there's a difference between the two.


Comments on this page:

By nanaya at 2024-10-22 06:59:21:

Is there actually a bourne shell that supports $(...) command substitution? I'm pretty sure whatever came with opensolaris didn't support it.

The other barebone shell I can think of is the one that comes with freebsd but it's ash, not bourne shell.

Unless you mean "bourne shell" to be "all bourne-like shells except bourne shell itself"...

By cks at 2024-10-22 10:07:08:

The $(...) form of command substitution is from POSIX (cf), and so almost any Bourne shell that wants to be POSIX compatible will support it. Only very old and peculiar versions of sh aren't POSIX compliant these days, so in practice /bin/sh is POSIX compliant almost everywhere. Solaris was one of the exceptions that refused to make their /bin/sh a POSIX shell and so made everyone suffer. These days such holdout Unixes simply aren't worth considering, unless you're being paid money to put up with them.

(And in this specific case, the same issue is present with the older `...` command substitution syntax. Using $(...) is just clearer and easier to write about.)

By nanaya at 2024-10-22 23:44:58:

/bin/sh on linux isn't bourne shell though? it's usually either dash, bash, or busybox (or something else that's not bourne shell). neither is on freebsd (ash) and openbsd (ksh). the compatibility mode is usually called posix mode, not bourne shell mode.

to me bourne shell is always for one that predates posix and use posix shell to denote those that is.

By cks at 2024-10-24 00:18:51:

For a long time, "Bourne shell" has broadly meant shells following the Bourne shell syntax, whether they were (increasingly drastically modified) versions of the V7 sh or reimplementations. Bash is a Bourne shell, as are dash and ash or anything that could be used as '/bin/sh'. Some Bourne shells are POSIX compatible, but not all of them (proprietary Unix vendors were prone to this for historical reasons), and also non-POSIX extensions are reasonably popular (Bash obviously has a lot of them, but so does ksh).

(Originally people might have said "Bourne compatible shell", but the 'compatible' soon routinely got omitted. Whether or not ksh is considered a Bourne shell or its own thing is somewhat fuzzy, but it is Bourne compatible, and a lot of the POSIX additions to V7 sh are from ksh.)

There once was a time when people had to care about what 'Bourne shell' features dated from V7 and so were available to scripts that wanted to be extremely portable, and which ones were POSIX and were not safe to use in extremely portable shell scripts. Those days are over. If you have to deal with an environment where 'sh' lacks $(...) or other POSIX additions, in practice you're on your own; a lot of shell scripts will blow up and a lot of 'Bourne shell' programming advice on the Internet will give you various errors.

PS: technically the OpenBSD ksh is not the real Korn shell, any more than dash or ash are the real Bourne shell; it's an independent open source reimplementation (or one strand of the development of that reimplementation).

Written on 21 October 2024.
« Two visions of 'software supply chain security'
Having rate-limits on failed authentication attempts is reassuring »

Page tools: View Source, View Normal.
Search:
Login: Password:

Last modified: Mon Oct 21 22:49:58 2024
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.