What is the benefit of using $() instead of backticks in shell scripts? [duplicate]
Asked Answered
D

9

251

There are two ways to capture the output of command line in bash:

  1. Legacy Bourne shell backticks ``:

    var=`command`
    
  2. $() syntax (which as far as I know is Bash specific, or at least not supported by non-POSIX old shells like original Bourne)

    var=$(command)
    

Is there any benefit to using the second syntax compared to backticks? Or are the two fully 100% equivalent?

Damien answered 26/2, 2012 at 1:37 Comment(6)
$() is POSIX and supported by all modern Bourne shells, e.g. ksh, bash, ash, dash, zsh, busybox, you name it. (A not so modern one is Solaris /bin/sh, but on Solaris you would make sure to use the modern /usr/xpg4/bin/sh instead).Mooned
Also, a note about using $() and backticks in aliases. If you have alias foo=$(command) in your .bashrc then command will be executed when the alias command itself is run during .bashrc interpretation. With alias foo=`command`, command will be executed each time the alias is run. But if you escape the $ with the $() form (e.g. alias foo=\$(command)), it too will execute each time the alias is run, instead of during .bashrc interpretation. As far as I can tell by testing, anyway; I can't find anything in the bash docs which explain this behavior.Barbarize
@Barbarize Which shell is this, I have tested bash and POSIX shell, the backtick does get executed when I source. Simple example: alias curDate=`date` After I source it and run curDate, then I get message it cannot find command Mon (Sourced on Monday), for example.Croatia
@Barbarize It's not true. Even with alias foo=`command` command is executed only one time. I checked it: function aaa() { printf date; echo aaa >> ~/test.txt; } alias test1=aaa. Function aaa is executing only one time (after every login) no matter how many times alias (test1) was executed. I used .bashrc (on Debian 10).Brecher
No idea, it was whatever version of bash I was using five and a half years ago, which was probably pretty old even by that point. It's possible my testing was incorrect, but it hardly matters now because I doubt anyone is still using whatever version that was.Barbarize
Does this answer your question? What is the difference between $(command) and `command` in shell programming?Erase
I
245

The major one is the ability to nest them, commands within commands, without losing your sanity trying to figure out if some form of escaping will work on the backticks.

An example, though somewhat contrived:

deps=$(find /dir -name $(ls -1tr 201112[0-9][0-9]*.txt | tail -1l) -print)

which will give you a list of all files in the /dir directory tree which have the same name as the earliest dated text file from December 2011 (a).

Another example would be something like getting the name (not the full path) of the parent directory:

pax> cd /home/pax/xyzzy/plugh
pax> parent=$(basename $(dirname $PWD))
pax> echo $parent
xyzzy

(a) Now that specific command may not actually work, I haven't tested the functionality. So, if you vote me down for it, you've lost sight of the intent :-) It's meant just as an illustration as to how you can nest, not as a bug-free production-ready snippet.

Isle answered 26/2, 2012 at 1:40 Comment(5)
I expect all code on SO to be production ready snippets, designed to NASA shuttle code reliability standards. Anything less gets a flag and a delete vote.Damien
@Damien In case you're not joking, I disagree that code contributions should be flagged for failing to automatically relicense away from SO defaults (CC-BY-SA or MIT licenses) in order to allow for such warranties or fitness of purpose. I would instead reuse code on SO at my own risk, and vote contributions according to helpfulness, technical merits, etc.Bearskin
@chrstphrchvz, if you look at my profile, you'll see this little snippet: "All code I post on Stack Overflow is covered by the "Do whatever the heck you want with it" licence, the full text of which is: Do whatever the heck you want with it." :-) In any case, I'm pretty certain it was humour from DVK.Isle
but what about if it was "cd /home/pax/xyzzy/plover"? Would you find yourself in a maze of twisty little passages, all different?Housewares
@wchargin did you know that this incident was a primary motivator for the addition of used defined literals to the C++ language ? en.cppreference.com/w/cpp/language/user_literalHorror
L
81

Suppose you want to find the lib directory corresponding to where gcc is installed. You have a choice:

libdir=$(dirname $(dirname $(which gcc)))/lib

libdir=`dirname \`dirname \\\`which gcc\\\`\``/lib

The first is easier than the second - use the first.

Logo answered 26/2, 2012 at 2:3 Comment(3)
It would be good to see some quotes around those command substitutions!Adao
At least for bash, the comment of @TomFenech doesn't apply to assignments. x=$(f); x=`f` behave the same as x="$(f)"; x="`f`". In contrast, array assignment x=($(f)); x=(`f`) do perform splitting at $IFS characters as expected when invoking commands. This is convenient (x=1 2 3 4 doesn't make sense) but inconsistent.Amalamalbena
@Amalamalbena you're right about the x=$(f) working without quotes. I should've been more specific; I was proposing to use libdir=$(dirname "$(dirname "$(which gcc)")")/lib (quotes around the inner command substitutions). If left unquoted, you are still subject to the usual word splitting and glob expansion.Adao
C
53

The backticks (`...`) is the legacy syntax required by only the very oldest of non-POSIX-compatible bourne-shells and $(...) is POSIX and more preferred for several reasons:

  • Backslashes (\) inside backticks are handled in a non-obvious manner:

    $ echo "`echo \\a`" "$(echo \\a)"
    a \a
    $ echo "`echo \\\\a`" "$(echo \\\\a)"
    \a \\a
    # Note that this is true for *single quotes* too!
    $ foo=`echo '\\'`; bar=$(echo '\\'); echo "foo is $foo, bar is $bar" 
    foo is \, bar is \\
    
  • Nested quoting inside $() is far more convenient:

    echo "x is $(sed ... <<<"$y")"
    

    instead of:

    echo "x is `sed ... <<<\"$y\"`"
    

    or writing something like:

    IPs_inna_string=`awk "/\`cat /etc/myname\`/"'{print $1}' /etc/hosts`
    

    because $() uses an entirely new context for quoting

    which is not portable as Bourne and Korn shells would require these backslashes, while Bash and dash don't.

  • Syntax for nesting command substitutions is easier:

    x=$(grep "$(dirname "$path")" file)
    

    than:

    x=`grep "\`dirname \"$path\"\`" file`
    

    because $() enforces an entirely new context for quoting, so each command substitution is protected and can be treated on its own without special concern over quoting and escaping. When using backticks, it gets uglier and uglier after two and above levels.

    Few more examples:

    echo `echo `ls``      # INCORRECT
    echo `echo \`ls\``    # CORRECT
    echo $(echo $(ls))    # CORRECT
    
  • It solves a problem of inconsistent behavior when using backquotes:

    • echo '\$x' outputs \$x
    • echo `echo '\$x'` outputs $x
    • echo $(echo '\$x') outputs \$x
  • Backticks syntax has historical restrictions on the contents of the embedded command and cannot handle some valid scripts that include backquotes, while the newer $() form can process any kind of valid embedded script.

    For example, these otherwise valid embedded scripts do not work in the left column, but do work on the rightIEEE:

    echo `                         echo $(
    cat <<\eof                     cat <<\eof
    a here-doc with `              a here-doc with )
    eof                            eof
    `                              )
    
    
    echo `                         echo $(
    echo abc # a comment with `    echo abc # a comment with )
    `                              )
    
    
    echo `                         echo $(
    echo '`'                       echo ')'
    `                              )
    

Therefore the syntax for $-prefixed command substitution should be the preferred method, because it is visually clear with clean syntax (improves human and machine readability), it is nestable and intuitive, its inner parsing is separate, and it is also more consistent (with all other expansions that are parsed from within double-quotes) where backticks are the only exception and ` character is easily camouflaged when adjacent to " making it even more difficult to read, especially with small or unusual fonts.

Source: Why is $(...) preferred over `...` (backticks)? at BashFAQ

See also:

Chaing answered 23/10, 2015 at 11:36 Comment(1)
Nested quoting in accent gravis-style substitution is actually undefined, you can use double quotes either outside or inside but not both, portably. Shells interpret them differently; some require a backslash to escape them, some require they not be backslash-escaped.Colwin
D
29

From man bash:

       $(command)
or
       `command`

Bash performs the expansion by executing command and replacing the com-
mand  substitution  with  the  standard output of the command, with any
trailing newlines deleted.  Embedded newlines are not deleted, but they
may  be  removed during word splitting.  The command substitution $(cat
file) can be replaced by the equivalent but faster $(< file).

When the old-style backquote form of substitution  is  used,  backslash
retains  its  literal  meaning except when followed by $, `, or \.  The
first backquote not preceded by a backslash terminates the command sub-
stitution.   When using the $(command) form, all characters between the
parentheses make up the command; none are treated specially.
Delmore answered 26/2, 2012 at 1:41 Comment(0)
M
12

In addition to the other answers,

$(...)

stands out visually better than

`...`

Backticks look too much like apostrophes; this varies depending on the font you're using.

(And, as I just noticed, backticks are a lot harder to enter in inline code samples.)

Martell answered 26/2, 2012 at 1:58 Comment(6)
you must have a weird keyboard (or I do?). For me, it's a lot easier to type backticks - they are the top left corner key, no SHIFT or ALT required.Damien
@DVK: I was talking about their appearance, not ease of typing. (My keyboard is probably the same as yours.) Still, now that you mention it, I think I have better muscle memory for $ ( and ) than I do for backtick; YMMV.Martell
never programmed in bash (skipped from old ksh to Perl) so definitely no memory for that specific syntax :)Damien
@DVK, I thought Keith was referring to the fact that non-block code here (block code means using four spaces at the start of the line) uses backticks to indicate it, making it difficult to put backticks in them, another illustration of the nesting difficulties :-) FWIW, you may find the code and /code tags (the other way of doing non-block code) can more easily contain backticks.Isle
@Pax - got it. Duh! I was indeed mentally stuck on blockcodes for some reason.Damien
I'm curious why this answer is getting downvotes. Is there something I could improve?Martell
K
9

$() allows nesting.

out=$(echo today is $(date))

I think backticks does not allow it.

Kung answered 26/2, 2012 at 1:44 Comment(1)
You can nest backticks; it is much harder: out=`echo today is \`date\`` .Logo
I
5

It is the POSIX standard that defines the $(command) form of command substitution. Most shells in use today are POSIX compliant and support this preferred form over the archaic backtick notation. The command substitution section (2.6.3) of the Shell Language document describes this:

Command substitution allows the output of a command to be substituted in place of the command name itself.  Command substitution shall occur when the command is enclosed as follows:

$(command)

or (backquoted version):

`command`

The shell shall expand the command substitution by executing command in a subshell environment (see Shell Execution Environment) and replacing the command substitution (the text of command plus the enclosing "$()" or backquotes) with the standard output of the command, removing sequences of one or more <newline> characters at the end of the substitution. Embedded <newline> characters before the end of the output shall not be removed; however, they may be treated as field delimiters and eliminated during field splitting, depending on the value of IFS and quoting that is in effect. If the output contains any null bytes, the behavior is unspecified.

Within the backquoted style of command substitution, <backslash> shall retain its literal meaning, except when followed by: '$' , '`', or <backslash>. The search for the matching backquote shall be satisfied by the first unquoted non-escaped backquote; during this search, if a non-escaped backquote is encountered within a shell comment, a here-document, an embedded command substitution of the $(command) form, or a quoted string, undefined results occur. A single-quoted or double-quoted string that begins, but does not end, within the "`...`" sequence produces undefined results.

With the $(command) form, all characters following the open parenthesis to the matching closing parenthesis constitute the command. Any valid shell script can be used for command, except a script consisting solely of redirections which produces unspecified results.

The results of command substitution shall not be processed for further tilde expansion, parameter expansion, command substitution, or arithmetic expansion. If a command substitution occurs inside double-quotes, field splitting and pathname expansion shall not be performed on the results of the substitution.

Command substitution can be nested. To specify nesting within the backquoted version, the application shall precede the inner backquotes with <backslash> characters; for example:

\`command\`

The syntax of the shell command language has an ambiguity for expansions beginning with "$((", which can introduce an arithmetic expansion or a command substitution that starts with a subshell. Arithmetic expansion has precedence; that is, the shell shall first determine whether it can parse the expansion as an arithmetic expansion and shall only parse the expansion as a command substitution if it determines that it cannot parse the expansion as an arithmetic expansion. The shell need not evaluate nested expansions when performing this determination. If it encounters the end of input without already having determined that it cannot parse the expansion as an arithmetic expansion, the shell shall treat the expansion as an incomplete arithmetic expansion and report a syntax error. A conforming application shall ensure that it separates the "$(" and '(' into two tokens (that is, separate them with white space) in a command substitution that starts with a subshell. For example, a command substitution containing a single subshell could be written as:

$( (command) )

Inebriate answered 26/2, 2012 at 13:53 Comment(0)
G
5

I came up with a perfectly valid example of $(...) over `...`.

I was using a remote desktop to Windows running Cygwin and wanted to iterate over a result of a command. Sadly, the backtick character was impossible to enter, either due to the remote desktop thing or Cygwin itself.

It's sane to assume that a dollar sign and parentheses will be easier to type in such strange setups.

Gleich answered 4/12, 2015 at 11:10 Comment(1)
$(…) is also much easier to type on a non-US keyboard. E.g. on the German keyboard the key is next to backspace and you need to press shift to type it, followed by a space. If you do not type a space, you could end up with e.g. è, because they keyboard allows you to type French lettersFernand
E
2

Here in 2021 it is worth mentioning a curious fact as a supplement to the other answers.

The Microsoft DevOps YAML "scripting" for pipelines may include Bash tasks. However, the notation $() is used for referring to variables defined in the YAML context, so in this case backticks should be used for capturing the output of commands.

This is mostly a problem when copying scripting code into a YAML script since the DevOps preprocessor is very forgiving about nonexisting variables, so there will not be any error message.

Eddington answered 11/3, 2021 at 13:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.