ARG_MAX
| Shells
| whatshell
| portability
| permissions
| UUOC
| ancient
| -
| ../Various
| HOME
$@
"
| echo/printf
| set -e
| test
| tty defs
| tty chars
| $()
vs )
| IFS
| using siginfo
| nanosleep
| line charset
| locale
$( )
" command substitution vs. embedded ")
" This page doesn't serve any purpose but fun, it's just about a special point concerning the robustness of shell parsers.
2016-04-17 (see recent changes)
In some shells, $( )
collides with other, "unbalanced", yet valid, right parentheses.
And there are some more subtle issues.
The much more famous issue, $( )
vs. `
...`
,
is mentioned below in the additional end notes.
The
SUSv3 rationale
and
SUSv4 rationale
to shell commands, C.2.9, about
"case x in (x)
" embedded in "$( )
":
$()
"
However, there are some cornercases for "$( )
"
even in shells which try to implement posix.
The following table lists the results for some testcases.
These tests exploit the case
command and here-documents.
They were taken from the above URL or Usenet and the thread
<mailman.20673.1135786095.20277.bug-bash@gnu.org>
in gnu.bash.bug (Dan Jacobson and Eric Blake). Jilles Tjoelker pointed out the problem with '# \'.
The table below unfortunately suggested wrong behaviour as correct for test D.2 until Robert Elz kindly pointed out that it should fail.
$( )
,
see full testcases
A.1 | "case x in x)" |
A.2 | "case x in x)" with a comment |
A.3 | "case x in (x)" |
A.4 | "case x in (x)" with a comment |
A.5 | "case x in (x)" with \n and without ";;" before "esac" |
B | ) in quotes |
C | ) after a comment sign |
D.1 | ) embedded in a here-document |
D.2 | ) embedded in a here-document, with \() inside |
D.3 | ) embedded in a here-document, eof and closing ) on same line According to POSIX/SUSv1-v4 it should fail with unterminated here-doc (see 2.7.4). |
D.4 | eof and closing ) on same line, combined with another correct terminator |
E | ) as delimiter for an embedded here-document (academic) |
F | unbalanced quote in an embedded here-document |
G | \ at end of line |
H | empty |
Shell | A.1 | A.2 | A.3 | A.4 | A.5 | B | C | D.1 | D.2 | D.3 | D.4 | E | F | G | H |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ash (original release) | + | + | - | - | - | + | + | + | + | + | - | + | + | + | - |
ash (early variants) | + | + | - | - | - | + | + | + | + | + | - | + | + | + | + |
ash (BSD/OS, NetBSD, FreeBSD, dash, slackware, busybox, Minix) | + | + | + | + | + | + | + | + | + | + | - | + | + | + | + |
bash-1.14.7/2.05/3.0 | - | - | + | + | + | + | - | - | - | + | - | - | - | + | + |
bash-3.1.0 | - | - | + | + | + | + | - | - | - | + | - | - | - | - | + |
bash-3.2.0 | - | - | + | + | + | + | + | - | - | + | - | - | - | - | + |
bash-4.0.10 | + | + | + | + | - | + | + | + | + | + | - | + | + | - | + |
bash-4.0.18/-4.0.35 | + | + | + | + | + | + | + | + | - | + | - | + | + | - | + |
bash-4.1.0/-4.1.7 | + | + | + | + | + | + | + | + | + | uw | - | + | + | - | + |
bash-4.2.0 | + | + | + | + | + | + | + | + | + | uw | - | + | + | + | + |
bosh-2016-02-02 | + | + | + | + | + | + | + | + | + | + | - | + | + | + | + |
ksh86 | - | - | - | - | - | + | - | - | - | + | - | - | - | + | + |
ksh88-a / e / i / i(SunOS xpg4/posix variant) | - | - | + | + | + | + | - | - | _ | + | - | - | - | + | + |
ksh93-d | + | + | + | + | + | + | + | + | + | + | - | + | + | - | + |
ksh93-k / t | + | + | + | + | + | + | + | + | + | + | - | + | + | + | + |
pdksh-5.2.14 | - | - | + | + | + | + | - | - | - | + | - | - | - | + | + |
posh-0.5.4/0.6.17 | - | - | + | + | + | + | - | - | - | + | - | - | - | + | + |
mksh-R28/R39 | - | - | + | + | + | + | - | - | - | + | - | - | - | + | + |
mksh-R52b | + | + | + | + | + | + | + | + | + | u | - | + | + | + | + |
zsh-3.0.0/4.3.9 | - | - | + | + | + | + | - | - | - | + | - | - | - | + | + |
case
command yet. Later ash variants usually accept the opening paren, e.g.,
BSD/OS 3.0, NetBSD 4.0, FreeBSD 4.1/5.1, (d)ash 0.3.5-4, Slackware 8.1, Minix 3.1.3, Busybox from birth.
The ash parser doesn't need this workaround. However, this is also an issue of script portability.
$( )
" yet and thus had not implemented the opening paren in a case pattern, yet.
$( )
vs. `...`
pro $()
form:
Without doubt, the "$( )
" syntax is the cleaner design because there are no escaping
rules to consider. And as \
is special in `...`
, you even have to escape itself twice.
Why was command substitution implemented differently, at all, in the first time? One possible argument:
You need a recursive parser for $()
which might have been too expensive
at the time of 6th edition.
Another argument: backquotes are easily confused with apostrophes -- not so much when actually using them (in terminals with fixed width fonts), but especially in other sources of documentation using arbitrary fonts.
weak pro:
An often mentioned advantage of the "$( )
" syntax:
it allows clean nesting. However, that's a weak argument: If you really have to nest command substitution,
then you might want to split into separate commands to improve readability and to ease maintenance.
contra:
The "$( )
" form is not traditionally portable
because traditional Bourne shells only implement the `...`
form.
But the more the traditional Bourne shell disappears, the weaker this arguments becomes.
academic contra:
Parsing problems mentioned on this page, see above.
An issue in ksh88, see below.
In ksh88, at least from release a to i, you have to be aware
of a subtle quoting issue inside $( )
.
Single quotes in embedded here-documents are converted to double quotes.
This means that
echo $(cat << EOF "double quotes" 'single quotes' EOF )results in
"double quotes" "single quotes"This happens for the
$( )
form but not for the ``
form.