What is the difference between $(command) and `command` in shell programming? - bash

To store the output of a command as a variable in sh/ksh/bash, you can do either
var=$(command)
or
var=`command`
What's the difference if any between the two methods?

The backticks/gravemarks have been deprecated in favor of $() for command substitution because $() can easily nest within itself as in $(echo foo$(echo bar)). There are other differences such as how backslashes are parsed in the backtick/gravemark version, etc.
See BashFAQ/082 for several reasons to always prefer the $(...) syntax.
Also see the POSIX spec for detailed information on the various differences.

They behave the same. The difference is syntactical: it's easier to nest $() than ``:
listing=$(ls -l $(cat filenames.txt))
vs.
listing=`ls -l \`cat filenames.txt\``

July 2014: The commit f25f5e6 (by Elia Pinto (devzero2000), April 2014, Git 2.0) adds to the nesting issue:
The backquoted form is the traditional method for command substitution, and is supported by POSIX.
However, all but the simplest uses become complicated quickly.
In particular, embedded command substitutions and/or the use of double quotes require
careful escaping with the backslash character.
That is why the git/Documentation/CodingGuidelines mentions:
We prefer $( ... ) for command substitution; unlike ``, it properly nests.
It should have been the way Bourne spelled it from day one, but unfortunately isn't.
thiton commented:
That is why `echo `foo`` won't work in general because of the inherent ambiguity because each ``can be opening or closing.
It might work for special cases due to luck or special features.
Update January 2016: Git 2.8 (March 2016) gets rid of backticks entirely.
See commit ec1b763, commit 9c10377, commit c7b793a, commit 80a6b3f, commit 9375dcf, commit e74ef60, commit 27fe43e, commit 2525c51, commit becd67f, commit a5c98ac, commit 8c311f9, commit 57da049, commit 1d9e86f, commit 78ba28d, commit efa639f, commit 1be2fa0, commit 38e9476, commit 8823d2f, commit 32858a0, commit cd914d8 (12 Jan 2016) by Elia Pinto (devzero2000).
(Merged by Junio C Hamano -- gitster -- in commit e572fef, 22 Jan 2016)
From Git 2.8 onwards, it is all $(...), no more `...`.

When the older back-tick form is used, backslash retains its literal meaning except when followed by $, `, or \. The first back-tick not preceded by a backslash terminates the command substitution.
When using the newer $(command) form, all characters between the parentheses make up the command; none are treated specially.
Both forms can be nested, but the back-tick variety requires the following form.
`echo \`foo\``
As opposed to:
$(echo $(foo))

There is little difference, except for what unescaped characters you can use inside of the command. You can even put `...` commands inside $(...) ones (and vice versa) for a more complicated two-level-deep command substitution.
There is a slightly different interpretation of the backslash character/operator. Among other things, when nesting `...` substitution commands, you must escape the inner ` characters with \, whereas with $() substition it understands the nesting automatically.

"What's the difference if any between the two methods?"
Make attention to this behaviour:
A="A_VARIABLE"
echo "$(echo "\$A")"
echo "`echo "\$A"`"
You will get these results:
$A
A_VARIABLE

Related

Why do people use grave accent instead of dollar and parentheses in g++? [duplicate]

To store the output of a command as a variable in sh/ksh/bash, you can do either
var=$(command)
or
var=`command`
What's the difference if any between the two methods?
The backticks/gravemarks have been deprecated in favor of $() for command substitution because $() can easily nest within itself as in $(echo foo$(echo bar)). There are other differences such as how backslashes are parsed in the backtick/gravemark version, etc.
See BashFAQ/082 for several reasons to always prefer the $(...) syntax.
Also see the POSIX spec for detailed information on the various differences.
They behave the same. The difference is syntactical: it's easier to nest $() than ``:
listing=$(ls -l $(cat filenames.txt))
vs.
listing=`ls -l \`cat filenames.txt\``
July 2014: The commit f25f5e6 (by Elia Pinto (devzero2000), April 2014, Git 2.0) adds to the nesting issue:
The backquoted form is the traditional method for command substitution, and is supported by POSIX.
However, all but the simplest uses become complicated quickly.
In particular, embedded command substitutions and/or the use of double quotes require
careful escaping with the backslash character.
That is why the git/Documentation/CodingGuidelines mentions:
We prefer $( ... ) for command substitution; unlike ``, it properly nests.
It should have been the way Bourne spelled it from day one, but unfortunately isn't.
thiton commented:
That is why `echo `foo`` won't work in general because of the inherent ambiguity because each ``can be opening or closing.
It might work for special cases due to luck or special features.
Update January 2016: Git 2.8 (March 2016) gets rid of backticks entirely.
See commit ec1b763, commit 9c10377, commit c7b793a, commit 80a6b3f, commit 9375dcf, commit e74ef60, commit 27fe43e, commit 2525c51, commit becd67f, commit a5c98ac, commit 8c311f9, commit 57da049, commit 1d9e86f, commit 78ba28d, commit efa639f, commit 1be2fa0, commit 38e9476, commit 8823d2f, commit 32858a0, commit cd914d8 (12 Jan 2016) by Elia Pinto (devzero2000).
(Merged by Junio C Hamano -- gitster -- in commit e572fef, 22 Jan 2016)
From Git 2.8 onwards, it is all $(...), no more `...`.
When the older back-tick form is used, backslash retains its literal meaning except when followed by $, `, or \. The first back-tick not preceded by a backslash terminates the command substitution.
When using the newer $(command) form, all characters between the parentheses make up the command; none are treated specially.
Both forms can be nested, but the back-tick variety requires the following form.
`echo \`foo\``
As opposed to:
$(echo $(foo))
There is little difference, except for what unescaped characters you can use inside of the command. You can even put `...` commands inside $(...) ones (and vice versa) for a more complicated two-level-deep command substitution.
There is a slightly different interpretation of the backslash character/operator. Among other things, when nesting `...` substitution commands, you must escape the inner ` characters with \, whereas with $() substition it understands the nesting automatically.
"What's the difference if any between the two methods?"
Make attention to this behaviour:
A="A_VARIABLE"
echo "$(echo "\$A")"
echo "`echo "\$A"`"
You will get these results:
$A
A_VARIABLE

Weird issue when running grep with the --include option

Here is the code at the bash shell. How is the file mask supposed to be specified, if not this way? I expected both commands to find the search expression, but it's not happening. In this example, I know in advance that I prefer to restrict the search to python source code files only, because unqualified searches are silly time wasters.
So, this works as expected:
grep -rni '/home/ga/projects' -e 'def Pr(x,u,v)'
/home/ga/projects/anom/anom.py:27:def Pr(x,u,v): blah, blah, ...
but this won't work:
grep --include=\*.{py} -rni '/home/ga/projects' -e 'def Pr(x,u,v)'
I'm using GNU grep version 2.16.
--include=\*.{py} looks like a broken attempt to use brace expansion (an unquoted {...} expression).
However, for brace expansion
to occur in bash (and ksh and zsh), you must either have:
a list of at least 2 items, separated with ,; e.g. {py,txt}, which expands to 2 arguments, py and txt.
or, a range of items formed from two end points, separated with ..; e.g., {1..3}, which expands to 3 arguments, 1, 2, and 3.
Thus, with a single item, simply do not use brace expansion:
--include=\*.py
If you did have multiple extensions to consider, e.g., *.py as well as *.pyc files, here's a robust form that illustrates the underlying shell features:
'--include=*.'{py,pyc}
Here:
Brace expansion is applied, because {...} contains a 2-item list.
Since the {...} directly follows the literal (single-quoted) string --include=*., the results of the brace expansion include the literal part.
Therefore, 2 arguments are ultimately passed to grep, with the following literal content:
--include=*.py
--include=*.pyc
Your command fails because of the braces '{}'. It will search for it in the file name. You can create a file such as 'myscript.{py}' to convince yourself. You'll see it will appear in the results.
The correct option parameter would be '*.py' or the equivalent \*.py. Either way will protect it from being (mis)interpreted by the shell.
On the other side, I can only advise to use the command find for such jobs :
find /home/ga/projects -regex '.*\.py$' -exec grep -e "def Pr(x,u,v)" {} +
That will protect you from hard to understand shell behaviour.
Try like this (using quotes to be safe; also better readability than backslash escaping IMHO):
grep --include='*.py' ...
your \*.{py} brace expansion usage isn't supported at all by grep. Please see the comments below for the full investigation regarding this. For the record, blame this answer for the resulting brace wars ;)
By the way, the brace expansion works generally fine in Bash. See mklement0 answer for more details.
Ack. As an alternative, you might consider switching to ack instead from now on. It's a tool just like grep, but fully optimized for programmers.
It's a great fit for what you are doing. A nice quote about it:
Every once in a while something comes along that improves an idea so much, you can't ignore it. Such a thing is ack, the grep replacement.

Why does bash brace expansion work in some arithmetic expressions but not others?

I am working on a very simple bash script and I'm having a problem with understating why deprecated $[] is working flawlessly, while $(()) seems to break the whole thing.
The code I'm referring to is:
for i in {1..10};
do
printf %4d $[{1..10}*i]
echo
done
In this version I am having no issues, yet I wouldn't like to use deprecated bash elements, that's why I wanted to switch to $(()).
Unfortunately, as soon as I change my code to:
printf %4d $(({1..10}*i))
I receive an error:
./script_bash.sh: line 8: {1..10}*i: syntax error: argument expected (error token is "{1..10}*i")
I'd be thankful for some help with this one...
Setting the way back machine for 1990.
Bash implemented the $[] syntax per POSIX P1003.2d9 (circa 1990), which was a draft of the released P1003.2-1992. In the two years between draft and standard, POSIX had instead settled on the ksh88 $(()) syntax and behaviors. Chet Ramey (bash maintainer) had this to say, back in 2012:
Bash... implemented $[...] because there was no other
syntax at the time, and to gain some operational experience with
arithmetic expansion in the shell. Bash-1.14... lists both forms of arithmetic expansion, but by
the time bash-2.0 was released in 1995, the manual referred only to
the $((...)) form.
This suggests to me that the $[] form was experimental, and it had certain behaviors (like brace expansion) that were specified into oblivion when POSIX adopted the $(()) syntax. Those experimental behaviors were left in, since there were already scripts in the wild relying on them (remember more than 2 years had elapsed).
Chet makes clear in that same thread that the $[] form is obsolete, but not deprecated:
Now, it's hardly any problem to keep dragging the $[...] syntax along.
It takes only a few dozen bytes of code. I have no plans to remove it.
The current POSIX standard, C.2.6 Word Expansions > Arithmetic Expansion mentions the syntax (emphasis mine):
In early proposals, a form $[expression] was used. It was functionally equivalent to the "$(())" of the current text, but objections were lodged that the 1988 KornShell had already implemented "$(())" and there was no compelling reason to invent yet another syntax. Furthermore, the "$[]" syntax had a minor incompatibility involving the patterns in case statements.
So the as-implemented behavior in bash isn't quite to specification, but since there are no plans to remove it, I see no reason to forgo its benefits if it neatly solves your problem. However, as pointed out by #Barmar's comment, it'd be A Good Idea to comment the code and link it here so future developers know what the heck you mean!
$(()) is for arithmetic expressions, and brace expansion isn't done in arithmetic.
Make an array with a loop:
for i in {1..10}
do
vals=()
for j in {1..10}
do
vals+=($((i*j)))
done
printf "%4d" ${vals[#]}
done
printf %4d $(({1..10}*i))
does not work because of the order in which parameters are expanded in bash. The brace expansion ({}) is done earlier than arithmetic expansion ($(())) by bash. Your code would definitely work if it were other way around.
From man bash:
The order of expansions is: brace expansion; tilde expansion, parameter and variable expansion, arithmetic expansion, and command substitution (done in a left-to-right fashion); word splitting; and pathname expansion.
Ran across some bash like this. This question asks for explanation, rather than solution, but here would be a $(())-way of expressing this.
for i in {1..10}; do
printf %4d $(eval echo '$(('{1..10}'*i))')
echo
done
Brace expansion is inhibited inside an arithmetic expansion like it is for parameter expansion.
(bash manual)
To avoid conflicts with parameter expansion, the string ‘${’ is not
considered eligible for brace expansion, and inhibits brace expansion
until the closing ‘}’.

What's difference between `which port` and $(which port) [duplicate]

To store the output of a command as a variable in sh/ksh/bash, you can do either
var=$(command)
or
var=`command`
What's the difference if any between the two methods?
The backticks/gravemarks have been deprecated in favor of $() for command substitution because $() can easily nest within itself as in $(echo foo$(echo bar)). There are other differences such as how backslashes are parsed in the backtick/gravemark version, etc.
See BashFAQ/082 for several reasons to always prefer the $(...) syntax.
Also see the POSIX spec for detailed information on the various differences.
They behave the same. The difference is syntactical: it's easier to nest $() than ``:
listing=$(ls -l $(cat filenames.txt))
vs.
listing=`ls -l \`cat filenames.txt\``
July 2014: The commit f25f5e6 (by Elia Pinto (devzero2000), April 2014, Git 2.0) adds to the nesting issue:
The backquoted form is the traditional method for command substitution, and is supported by POSIX.
However, all but the simplest uses become complicated quickly.
In particular, embedded command substitutions and/or the use of double quotes require
careful escaping with the backslash character.
That is why the git/Documentation/CodingGuidelines mentions:
We prefer $( ... ) for command substitution; unlike ``, it properly nests.
It should have been the way Bourne spelled it from day one, but unfortunately isn't.
thiton commented:
That is why `echo `foo`` won't work in general because of the inherent ambiguity because each ``can be opening or closing.
It might work for special cases due to luck or special features.
Update January 2016: Git 2.8 (March 2016) gets rid of backticks entirely.
See commit ec1b763, commit 9c10377, commit c7b793a, commit 80a6b3f, commit 9375dcf, commit e74ef60, commit 27fe43e, commit 2525c51, commit becd67f, commit a5c98ac, commit 8c311f9, commit 57da049, commit 1d9e86f, commit 78ba28d, commit efa639f, commit 1be2fa0, commit 38e9476, commit 8823d2f, commit 32858a0, commit cd914d8 (12 Jan 2016) by Elia Pinto (devzero2000).
(Merged by Junio C Hamano -- gitster -- in commit e572fef, 22 Jan 2016)
From Git 2.8 onwards, it is all $(...), no more `...`.
When the older back-tick form is used, backslash retains its literal meaning except when followed by $, `, or \. The first back-tick not preceded by a backslash terminates the command substitution.
When using the newer $(command) form, all characters between the parentheses make up the command; none are treated specially.
Both forms can be nested, but the back-tick variety requires the following form.
`echo \`foo\``
As opposed to:
$(echo $(foo))
There is little difference, except for what unescaped characters you can use inside of the command. You can even put `...` commands inside $(...) ones (and vice versa) for a more complicated two-level-deep command substitution.
There is a slightly different interpretation of the backslash character/operator. Among other things, when nesting `...` substitution commands, you must escape the inner ` characters with \, whereas with $() substition it understands the nesting automatically.
"What's the difference if any between the two methods?"
Make attention to this behaviour:
A="A_VARIABLE"
echo "$(echo "\$A")"
echo "`echo "\$A"`"
You will get these results:
$A
A_VARIABLE

Bash variables with spaces

I'm facing the next problem in MinGW shell under windows. I have in my /etc/profile the expression:
export GIT_SSH="/c/Program Files/TortoiseGit/bin/TortoisePlink.exe"
This doesn't work when I use git fetch on the local repository. But if I do it like this (old DOS way), it works:
export GIT_SSH="/c/Progra~1/TortoiseGit/bin/TortoisePlink.exe"
My question is:
How can I make it work using spaces in the variable?
For testing purpose you can simulate something like this (any example is good):
export VAR="/c/Program Files/TortoiseGit/bin/TortoisePlink.exe"
# and try to execute like this
$VAR
Is there a solution for this (other than the previous mentioned)?
Execute it like this: "$VAR". This is one of the most significant gotchas in shell scripting because strings are always substituted literally and any contained spaces are treated as token delimiters rather than as characters of the string. Think of substituting a variable as a kind of code pasting at runtime.
What really happens when you write $VAR is that the shell tries to execute the binary /c/Program with a first argument Files/TortoiseGit/bin/TortoisePlink.exe.
I learned this the hard way by getting a strange syntax error in a big shell script for a particular input. No other languages I can think of can complain for syntax errors if the runtime input contains special characters - but that is the nature of shell scripting since command interpreters like bash and sh interpret the code line by line.
Whenever you expect a string to contain spaces and you don't want to treat it as separate tokens, enclose it in double quotes.
For reference, I solved a similar issue on osx by encapsulating the argument with escaped quotations. This may not be the best solution, but it seems to work.
alias sub="\"/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl\""
I've solved it by including a backslash to escape the space:
/Program Files becomes /Program\ Files
Example:
export GIT_SSH=/c/Program\ Files/TortoiseGit/bin/TortoisePlink.exe
With Git 2.23 (Q3 2019, eight years later), a GIT_SSH set to /c/Program\ Files/TortoiseGit/bin/TortoisePlink.exe will... work (for those still on Windows 7)!
See commit eb7c786 (16 Jul 2019) by Johannes Schindelin (dscho).
(Merged by Junio C Hamano -- gitster -- in commit a5194d8, 25 Jul 2019)
mingw: support spawning programs containing spaces in their names
On some older Windows versions (e.g. Windows 7), the CreateProcessW() function does not really support spaces in its first argument, lpApplicationName.
But it supports passing NULL as lpApplicationName, which makes it figure out the application from the (possibly quoted) first argument of lpCommandLine.
Let's use that trick (if we are certain that the first argument matches
the executable's path) to support launching programs whose path contains
spaces.
This fixes git-for-windows/git issue 692
Git 2.24 (Q4 2019) adds a test:
See commit 71f4960 (01 Oct 2019) by Alexandr Miloslavskiy (SyntevoAlex).
(Merged by Junio C Hamano -- gitster -- in commit 424663d, 09 Oct 2019)
t0061: fix test for argv[0] with spaces (MINGW only)
The test was originally designed for the case where user reported that setting GIT_SSH to a .bat file with spaces in path fails on Windows: git-for-windows#692
some dirty hack for commands with spaces in variables -
for i in `k get po --all-namespaces -o wide | grep 'CrashLoop\|ImagePull' | awk '{printf " -n#%s#scale#deployment/%s",$1,$2}'`; do $(echo "kubectl ${i%-*-*} --replicas=0" | sed 's/#/ /g'); done
so here i use # instead of space forming variable and use $(echo variavle | sed 's/#/ /g') to execute lines from sed with spaces
It is hackk but it is simplier than all "corect ways" and works for me

Resources