In shell-scripting, backticks are deprecated. They have been for a while. They're not deprecated in the sense that they're going to be removed any time soon. They're deprecated in the sense that the newer syntax has advantages over the old back-tick syntax and that the newer syntax should be preferred. Back-ticks are only really kept in bash for backwards compatibility with older, pre-existing scripts. But the current POSIX standard strongly recommends using the newer
$()
substitution syntax.
The main advantage of using the newer syntax is that it's much easier to create nested substitutions.
e.g.
Bash:
someVar=$(/path/to/script -infile "$(ls -1tr 202312[0-9][0-9]*.txt | tail -n 1)" -print0)
Above is a bit of a contrived example using two nested command substitutions.
I couldn't think of an actual practical/useful example offhand.
The nested substitution above uses
ls
to list all text files that start with a timestamp in December 2023. e.g. 20231201-somefile.txt, 20231203-anotherfile.txt and pipes the result to
tail
, to display the most recent file.
The output of that command is substituted in as the value for the
-infile
parameter for a script we're running. And the output of the script is substituted and assigned to a variable called
someVar
.
To do that purely with back-ticks you have to escape the inner back-ticks, to avoid closing the main, outer back-ticks.
Bash:
someVar=`/path/to/script -infile \`ls -1tr 202312[0-9][0-9]*.txt | tail -n 1\` -print`
In the above, we've escaped the back-ticks for the nested substitution.
And already, we can see that it's not entirely clear where each substitution begins and ends.
Now let's consider that we want to add another parameter to the script using another substitution, we could escape the back-ticks again to create a substitution for another parameter. That wouldn't be a problem.
For example, let's add a -compare option to the main script and we'll use the earliest time-stamped file from december 2023 as a parameter:
Bash:
someVar=`/path/to/script --infile \`ls -1tr 202312[0-9][0-9]*.txt | tail -n 1\` --compare \`ls -1tr 202312[0-9][0-9].txt | head -n 1\` -print`
OK - now we've got two substitutions nested inside the main substitution. But from looking at that line, it's really unclear what it's doing.
Whereas if we use the $() syntax:
Bash:
someVar=$(/path/to/script --infile "$(ls -1tr 202312[0-9][0-9]*.txt | tail -n 1)" --compare "$(ls -1tr 202312[0-9][0-9].txt | head -n 1)" -print)
Now we can see exactly where each substitution begins and ends.
What happens if we now want to nest a substitution inside an already nested substitution, using back-ticks?
For example IF the
ls
command took another parameter with a value that came from another script. (NOTE: Obviously
ls
DOESN'T have anything like that, but for this example - let's just imagine that it does.
Lets try to nest the result of another command as a fictional parameter to the
ls
command:
e.g. --not-real $(/path/to/anotherscript -a)
Using the $() syntax:
Bash:
someVar=$(/path/to/script --infile "$(ls -1tr 202312[0-9][0-9]*.txt --not-real $(/path/to/anotherscript -a) | tail -n 1)" --compare "$(ls -1tr 202312[0-9][0-9].txt | head -n 1)" -print)
With the nested $() substitutions, that's really easy to do.
Obviously, the ls command would fail IRL because it doesn't have a --not-real parameter, but for this example, we'll just imagine that it is a valid parameter and it does work.
But what about with back-ticks?
With back-ticks it looks like this:
Bash:
someVar=`/path/to/script --infile \`ls -1tr 202312[0-9][0-9]*.txt --not-real \`/path/to/anotherscript -a\` | tail -n 1\` --compare \`ls -1tr 202312[0-9][0-9].txt | head -n 1\` -print`
Now we have a real mess AND our substitutions will fail because the escaped back-tick after the fictional
--not-real
parameter (which we intended to be the opening back-tick for our new nested substitution) is actually going to be interpreted as the closing back-tick for the
ls
command that we're attempting to do a nested substitution in. So now we've broken our command.
With the back-ticks, there's a hard limit to the amount of nesting you can do. If you have nested substitutions using back-ticks, the more substitutions you have, the more ugly and unreadable the line becomes and the more likely it is that you'll make a mistake.
And that's the reason that the $() syntax was eventually introduced. Because if you do ever find yourself needing to multi-layered nested substitutions, you couldn't do that with the back-ticks.
When it comes to scripting - if you only have a single command substitution using a simple command, then you could get away with using back-ticks. Nobody's going to stop you. They are still supported, albeit in more of a legacy way.
And you could potentially mix and match back-ticks and $() substitutions to a certain extent, I suppose?!
e.g.
Bash:
someVar=`/path/to/script --param "$(/path/to/anotherscript -a)"`
But again, the newer $() syntax is more readable, it allows you to create much more complex and expressive nested substitutions (if you need to) and it's a lot less error prone than using back-ticks.
I hope this helps!