|
Page 1 of 2 Command Substitution in Shell scripting
Command substitution reassigns the output of a command [1] or even
multiple commands; it literally plugs the command output into
another context.
The classic form of command substitution uses backquotes (`...`).
Commands within backquotes (backticks) generate command line
text.
script_name=`basename $0`
echo "The name of this script is
$script_name."
The output of commands can be used as
arguments to another command, to set a variable, and even for
generating the argument list in a for loop.
rm `cat filename` # "filename" contains a
list of files to delete.
#
# S. C. points out that "arg list too
long" error might result.
# Better is xargs rm -- < filename
# ( -- covers those cases where "filename"
begins with a "-" )
textfile_listing=`ls *.txt`
# Variable contains names of all *.txt
files in current working directory.
echo $textfile_listing
textfile_listing2=$(ls *.txt) # The
alternative form of command substitution.
echo $textfile_listing2
# Same result.
# A possible problem with putting a list
of files into a single string
# is that a newline may creep in.
#
# A safer way to assign a list of files to
a parameter is with an array.
# shopt -s nullglob # If no match,
filename expands to nothing.
# textfile_listing=( *.txt )
#
# Thanks, S.C.
Note
Command substitution invokes a
subshell.
Caution
Command substitution may result in word
splitting.
COMMAND `echo a b` # 2 args: a and b
COMMAND "`echo a b`" # 1 arg: "a b"
COMMAND `echo` # no arg
COMMAND "`echo`" # one empty arg
# Thanks, S.C.
Even when there is no word splitting,
command substitution can remove trailing newlines.
# cd "`pwd`" # This should always
work.
# However...
mkdir 'dir with trailing newline
'
cd 'dir with trailing newline
'
cd "`pwd`" # Error message:
# bash: cd: /tmp/file with trailing
newline: No such file or directory
cd "$PWD" # Works fine.
old_tty_setting=$(stty -g) # Save old
terminal setting.
echo "Hit a key "
stty -icanon -echo # Disable "canonical"
mode for terminal.
# Also, disable *local* echo.
key=$(dd bs=1 count=1 2> /dev/null) #
Using 'dd' to get a keypress.
stty "$old_tty_setting" # Restore old
setting.
echo "You hit ${#key} key." # ${#variable}
= number of characters in $variable
#
# Hit any key except RETURN, and the
output is "You hit 1 key."
# Hit RETURN, and it's "You hit 0
key."
# The newline gets eaten in the command
substitution.
Thanks, S.C.
Caution
Using echo to output an unquoted variable
set with command substitution removes trailing newlines characters
from the output of the reassigned command(s). This can cause
unpleasant surprises.
dir_listing=`ls -l`
echo $dir_listing # unquoted
# Expecting a nicely ordered directory
listing.
# However, what you get is:
# total 3 -rw-rw-r-- 1 bozo bozo 30 May 13
17:15 1.txt -rw-rw-r-- 1 bozo
# bozo 51 May 15 20:57 t2.sh -rwxr-xr-x 1
bozo bozo 217 Mar 5 21:13 wi.sh
# The newlines disappeared.
echo "$dir_listing" # quoted
# -rw-rw-r-- 1 bozo 30 May 13 17:15
1.txt
# -rw-rw-r-- 1 bozo 51 May 15 20:57
t2.sh
# -rwxr-xr-x 1 bozo 217 Mar 5 21:13
wi.sh
Command substitution even permits setting
a variable to the contents of a file, using either redirection or
the cat command.
variable1=`<file1` # Set "variable1" to
contents of "file1".
variable2=`cat file2` # Set "variable2" to
contents of "file2".
# This, however, forks a new process,
#+ so the line of code executes slower
than the above version.
# Note:
# The variables may contain embedded
whitespace,
#+ or even (horrors), control
characters.
# Excerpts from system file,
/etc/rc.d/rc.sysinit
#+ (on a Red Hat Linux installation)
if [ -f /fsckoptions ]; then
fsckoptions=`cat /fsckoptions`
...
fi
#
#
if [ -e "/proc/ide/${disk[$device]}/media"
] ; then
hdmedia=`cat
/proc/ide/${disk[$device]}/media`
...
fi
#
#
if [ ! -n "`uname -r | grep -- "-"`" ];
then
ktag="`cat /proc/version`"
...
fi
#
#
if [ $usb = "1" ]; then
sleep 5
mouseoutput=`cat /proc/bus/usb/devices
2>/dev/null|grep -E "^I.*Cls=03.*Prot=02"`
kbdoutput=`cat /proc/bus/usb/devices
2>/dev/null|grep -E "^I.*Cls=03.*Prot=01"`
...
fi
Caution
Do not set a variable to the contents of a
long text file unless you have a very good reason for doing so. Do
not set a variable to the contents of a binary file, even as a
joke.
|