ARG_MAX| Shells | whatshell | portability | permissions | UUOC | ancient | - | ../Various | HOME
$@" | echo/printf | set -e | test | tty defs | tty chars |
)| 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.
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 <firstname.lastname@example.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|
|ash (original release)||+||+||-||-||-||+||+||+||+||+||-||+||+||+||-|
|ash (early variants)||+||+||-||-||-||+||+||+||+||+||-||+||+||+||+|
|ash (BSD/OS, NetBSD, FreeBSD, dash, slackware, busybox, Minix)||+||+||+||+||+||+||+||+||+||+||-||+||+||+||+|
|ksh88-a / e / i / i(SunOS xpg4/posix variant)||-||-||+||+||+||+||-||-||_||+||-||-||-||+||+|
|ksh93-k / t||+||+||+||+||+||+||+||+||+||+||-||+||+||+||+|
casecommand 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.
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.
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.
$( )" form is not traditionally portable
because traditional Bourne shells only implement the
But the more the traditional Bourne shell disappears, the weaker this arguments becomes.
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