Re: () $() etc., in Bash



2006-05-9, 18:49(-04), Chris F.A. Johnson:
On 2006-05-09, yusuf wrote:
what is $( ) ??

On 2006-05-09, Stephane CHAZELAS wrote:
2006-05-8, 13:48(-07), yusuf:

Also, would appreciate if someone could explain the different types and
usages in Bash for all the $(( .. )) , $[ ... ] etc.
[...]

$(...) is command substitution.

It is replaced on the command line by the output of the command
inside the parentheses.

That's a bit short, when unquoted and in list contexts, as in:

cmd -- $(cmd2)

or

for i in $(cmd2)

it is replaced on the command line by the result of the filename
generation (the process of expanding wildcard patterns into file
names) performed on the list of words that is obtained by
performing the word splitting (the process of taking a string
and chopping it into a list of words by a complex process
involving the $IFS special parameter and that is slightly
different from one shell to another) on the character string
that is the collected output of the command (as long as it
doesn't contain NUL characters) but where every trailing
linefeed character has been removed. (in zsh, the filename
generation is not performed).

In other words, if the cmd2 outputs:
" : *-* : bar baz:<LF><LF>" and IFS is ": ", then "cmd" will be
called with a number of arguments that depend on the shell and
the content of the current directory.

In the bourne shell (the syntax in the Bourne shell is `...`
instead of $(...)), that will be the list of files in the
current directory whose name contains a "-" but doesn't start
with a "." followed by "bar" and "baz".

In some Bourne like shells, it will be the same except that
there will be an empty element at the front, and with some
others both at the front and the tail. With zsh, the arguments
will be "", "*-*", "bar", "baz" and "".

When quoted or in scalar context, in other words, where it can't
expand to several words such as in:

cmd -- "$(cmd2)
var=$(cmd2)
case $(cmd2) in
.... and various others, that may depend on the shell such as
export var=$(cmd2) # only in bash and ksh
while
export $(echo a b) # export both a and b

Then, it is replaced with one string that is the collected
output (as long as it doesn't contain any NUL character) of the
command but where every trailing linefeed character has been
removed.

So, it is very important to understand that it is a very twisted
feature of the shell, to be used with care.

If one wants to have the output of a command in a single word
(argument), it is very important to quote it:

cmd -- "$(cmd2)"

If the fact that trailing linefeeds are removed is important,
such as is:

var=$(basename -- "$file")

Because the linefeed character can be found in a filename just
as any other character and possibly in trailing position, then a
workaround is:

var=$(basename -- "$file"; echo .)
var=${var%??}

if file is: "foo/bar<LF>", then basename outputs "bar<LF><LF>",
echo will output ".<LF>", so the whole output will be:
"bar<LF><LF>.<LF>" which the command substitution will shorten
to "bar<LF><LF>.". ${var%??} will remove two characters at the
end, so you'll be left with "bar<LF>" which is the basename of
the file.

When you want the output to be split into several words, then
almost certainly, you want to disable the filename generation as
in 99% of the cases, it doesn't make sense (that's a reason why
zsh disabled that weird /feature/ by default). And you have to
set the $IFS special variable, to specify how you want them to
be cut.

For instance, if a command outputs a list of strings, one per
line, you need to set the IFS parameter to a linefeed (newline)
character, and disable filename generation:

set -f # disable filename generation
IFS='
' # linefeed
for line in $(cmd)
do ...
done

(note that as the linefeed character is considered as a
/white-space/ character by the word splitting process, it
considers that any sequence of it makes one separator (and the
leading and trailing ones are discarded). In other words, no
empty element will result from the splitting, which means the
empty lines are discarded).

--
Stéphane
.



Relevant Pages

  • Re: Excercices
    ... > example or get a better shell. ... program (in cmd.exe it is an internal command), ... In the above example there is neither a escape character nor ... You are called "Phil" so you can be distinguished from other ...
    (alt.lang.asm)
  • comp.unix.shell FAQ - Answers to Frequently Asked Questions Part 2.
    ... character except '\0' is allowed in a file path on Unix. ... the default shell" isn't a good reason. ... command line buffer), and will fail if any file names contain a ... How do I use shell variables in awk scripts ...
    (comp.unix.shell)
  • RE: character injecting on linux console
    ... character into the stream as if it had been typed in"), ... > bash: 6c: command not found ... > some special sequences are able to inject characters into my terminal ... > when the shell returns back to my prompt I will find 2 characters in the ...
    (Vuln-Dev)
  • Re: Script to extract portions of text from a text file
    ... > There is no difference between a command processed at the command line ... > instructions one line at a time. ... > command line, or from a file, just like a shell. ... it has to read one character at a time. ...
    (comp.unix.shell)
  • Bash-4.0 available for FTP
    ... Unlike previous bash distributions, this tar file includes the formatted ... The shell has been changed to be more ... rigorous about parsing commands inside command substitutions, ... Changes have been made to the Readline library being released at ...
    (gnu.announce)