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.