9.3. Parameter Substitution

Manipulating and/or expanding variables

${parameter}

Same as $parameter, i.e., value of the variable parameter. In certain contexts, only the less ambiguous ${parameter} form works.

May be used for concatenating variables with strings.

your_id=${USER}-on-${HOSTNAME}




echo "$your_id"




#




echo "Old \$PATH = $PATH"




PATH=${PATH}:/opt/bin  #Add /opt/bin to $PATH for duration of script.




echo "New \$PATH = $PATH"

${parameter-default}, ${parameter:-default}

If parameter not set, use default.

echo ${username-`whoami`}




# Echoes the result of `whoami`, if variable $username is still unset.

Note

${parameter-default} and ${parameter:-default} are almost equivalent. The extra : makes a difference only when parameter has been declared, but is null.

#!/bin/bash









username0=




# username0 has been declared, but is set to null.




echo "username0 = ${username0-`whoami`}"




# Will not echo.









echo "username1 = ${username1-`whoami`}"




# username1 has not been declared.




# Will echo.









username2=




# username2 has been declared, but is set to null.




echo "username2 = ${username2:-`whoami`}"




# Will echo because of :- rather than just - in condition test.









exit 0

The default parameter construct finds use in providing "missing" command-line arguments in scripts.

DEFAULT_FILENAME=generic.data




filename=${1:-$DEFAULT_FILENAME}




#  If not otherwise specified, the following command block operates




#+ on the file "generic.data".




#




#  Commands follow.

See also Example 3-4, Example 29-2, and Example A-7.

Compare this method with using an and list to supply a default command-line argument.

${parameter=default}, ${parameter:=default}

If parameter not set, set it to default.

Both forms nearly equivalent. The : makes a difference only when $parameter has been declared and is null, [1] as above.

echo ${username=`whoami`}




# Variable "username" is now set to `whoami`.

${parameter+alt_value}, ${parameter:+alt_value}

If parameter set, use alt_value, else use null string.

Both forms nearly equivalent. The : makes a difference only when parameter has been declared and is null, see below.

echo "###### \${parameter+alt_value} ########"




echo









a=${param1+xyz}




echo "a = $a"      # a =









param2=




a=${param2+xyz}




echo "a = $a"      # a = xyz









param3=123




a=${param3+xyz}




echo "a = $a"      # a = xyz









echo




echo "###### \${parameter:+alt_value} ########"




echo









a=${param4:+xyz}




echo "a = $a"      # a =









param5=




a=${param5:+xyz}




echo "a = $a"      # a =




# Different result from   a=${param5+xyz}









param6=123




a=${param6+xyz}




echo "a = $a"      # a = xyz

${parameter?err_msg}, ${parameter:?err_msg}

If parameter set, use it, else print err_msg.

Both forms nearly equivalent. The : makes a difference only when parameter has been declared and is null, as above.

Example 9-13. Using parameter substitution and error messages

#!/bin/bash









# Check some of the system's environmental variables.




#  If, for example, $USER, the name of the person at the console, is not set,




#+ the machine will not recognize you.









: ${HOSTNAME?} ${USER?} ${HOME?} ${MAIL?}




  echo




  echo "Name of the machine is $HOSTNAME."




  echo "You are $USER."




  echo "Your home directory is $HOME."




  echo "Your mail INBOX is located in $MAIL."




  echo




  echo "If you are reading this message,"




  echo "critical environmental variables have been set."




  echo




  echo









# ------------------------------------------------------









#  The ${variablename?} construction can also check




#+ for variables set within the script.









ThisVariable=Value-of-ThisVariable




#  Note, by the way, that string variables may be set




#+ to characters disallowed in their names.




: ${ThisVariable?}




echo "Value of ThisVariable is $ThisVariable".




echo




echo














: ${ZZXy23AB?"ZZXy23AB has not been set."}




#  If ZZXy23AB has not been set,




#+ then the script terminates with an error message.









# You can specify the error message.




# : ${ZZXy23AB?"ZZXy23AB has not been set."}














# Same result with:    dummy_variable=${ZZXy23AB?}




#                      dummy_variable=${ZZXy23AB?"ZXy23AB has not been set."}




#




#                      echo ${ZZXy23AB?} >/dev/null



















echo "You will not see this message, because script terminated above."









HERE=0




exit $HERE   # Will *not* exit here.

Example 9-14. Parameter substitution and "usage" messages

#!/bin/bash




# usage-message.sh









: ${1?"Usage: $0 ARGUMENT"}




#  Script exits here if command-line parameter absent,




#+ with following error message.




#    usage-message.sh: 1: Usage: usage-message.sh ARGUMENT









echo "These two lines echo only if command-line parameter given."




echo "command line parameter = \"$1\""









exit 0  # Will exit here only if command-line parameter present.









# Check the exit status, both with and without command-line parameter.




# If command-line parameter present, then "$?" is 0.




# If not, then "$?" is 1.

Parameter substitution and/or expansion. The following expressions are the complement to the match in expr string operations (see Example 12-6). These particular ones are used mostly in parsing file path names.

Variable length / Substring removal

${#var}

String length (number of characters in $var). For an array, ${#array} is the length of the first element in the array.

Note

Exceptions:

  • ${#*} and ${#@} give the number of positional parameters.

  • For an array, ${#array[*]} and ${#array[@]} give the number of elements in the array.

Example 9-15. Length of a variable

#!/bin/bash




# length.sh









E_NO_ARGS=65









if [ $# -eq 0 ]  # Must have command-line args to demo script.




then




  echo "Invoke this script with one or more command-line arguments."




  exit $E_NO_ARGS




fi  









var01=abcdEFGH28ij









echo "var01 = ${var01}"




echo "Length of var01 = ${#var01}"









echo "Number of command-line arguments passed to script = ${#@}"




echo "Number of command-line arguments passed to script = ${#*}"









exit 0
${var#Pattern}, ${var##Pattern}

Remove from $var the shortest/longest part of $Pattern that matches the front end of $var.

A usage illustration from Example A-8:
# Function from "days-between.sh" example.




# Strips leading zero(s) from argument passed.









strip_leading_zero () # Better to strip possible leading zero(s)




{                     # from day and/or month




  val=${1#0}          # since otherwise Bash will interpret them




  return $val         # as octal values (POSIX.2, sect 2.9.2.1).




}

Another usage illustration:
echo `basename $PWD`        # Basename of current working directory.




echo "${PWD##*/}"           # Basename of current working directory.




echo




echo `basename $0`          # Name of script.




echo $0                     # Name of script.




echo "${0##*/}"             # Name of script.




echo




filename=test.data




echo "${filename##*.}"      # data




                            # Extension of filename.

${var%Pattern}, ${var%%Pattern}

Remove from $var the shortest/longest part of $Pattern that matches the back end of $var.

Version 2 of Bash adds additional options.

Example 9-16. Pattern matching in parameter substitution

#!/bin/bash




# Pattern matching  using the # ## % %% parameter substitution operators.









var1=abcd12345abc6789




pattern1=a*c  # * (wild card) matches everything between a - c.









echo




echo "var1 = $var1"           # abcd12345abc6789




echo "var1 = ${var1}"         # abcd12345abc6789   (alternate form)




echo "Number of characters in ${var1} = ${#var1}"




echo "pattern1 = $pattern1"   # a*c  (everything between 'a' and 'c')




echo














echo '${var1#$pattern1}  =' "${var1#$pattern1}"    #         d12345abc6789




# Shortest possible match, strips out first 3 characters  abcd12345abc6789




#                                     ^^^^^               |-|




echo '${var1##$pattern1} =' "${var1##$pattern1}"   #                  6789      




# Longest possible match, strips out first 12 characters  abcd12345abc6789




#                                    ^^^^^                |----------|









echo; echo









pattern2=b*9            # everything between 'b' and '9'




echo "var1 = $var1"     # Still  abcd12345abc6789




echo "pattern2 = $pattern2"




echo









echo '${var1%pattern2}  =' "${var1%$pattern2}"     #     abcd12345a




# Shortest possible match, strips out last 6 characters  abcd12345abc6789




#                                     ^^^^                         |----|




echo '${var1%%pattern2} =' "${var1%%$pattern2}"    #     a




# Longest possible match, strips out last 12 characters  abcd12345abc6789




#                                    ^^^^                 |-------------|









# Remember, # and ## work from the left end of string,




#           % and %% work from the right end.









echo









exit 0

Example 9-17. Renaming file extensions:

#!/bin/bash









#                 rfe




#                 ---









# Renaming file extensions.




#




#         rfe old_extension new_extension




#




# Example:




# To rename all *.gif files in working directory to *.jpg,




#          rfe gif jpg









ARGS=2




E_BADARGS=65









if [ $# -ne "$ARGS" ]




then




  echo "Usage: `basename $0` old_file_suffix new_file_suffix"




  exit $E_BADARGS




fi









for filename in *.$1




# Traverse list of files ending with 1st argument.




do




  mv $filename ${filename%$1}$2




  #  Strip off part of filename matching 1st argument,




  #+ then append 2nd argument.




done









exit 0

Variable expansion / Substring replacement

These constructs have been adopted from ksh.

${var:pos}

Variable var expanded, starting from offset pos.

${var:pos:len}

Expansion to a max of len characters of variable var, from offset pos. See Example A-15 for an example of the creative use of this operator.

${var/Pattern/Replacement}

First match of Pattern, within var replaced with Replacement.

If Replacement is omitted, then the first match of Pattern is replaced by nothing, that is, deleted.

${var//Pattern/Replacement}

Global replacement. All matches of Pattern, within var replaced with Replacement.

As above, if Replacement is omitted, then all occurrences of Pattern are replaced by nothing, that is, deleted.

Example 9-18. Using pattern matching to parse arbitrary strings

#!/bin/bash









var1=abcd-1234-defg




echo "var1 = $var1"









t=${var1#*-*}




echo "var1 (with everything, up to and including first - stripped out) = $t"




#  t=${var1#*-}  works just the same,




#+ since # matches the shortest string,




#+ and * matches everything preceding, including an empty string.




# (Thanks, S. C. for pointing this out.)









t=${var1##*-*}




echo "If var1 contains a \"-\", returns empty string...   var1 = $t"














t=${var1%*-*}




echo "var1 (with everything from the last - on stripped out) = $t"









echo









# -------------------------------------------




path_name=/home/bozo/ideas/thoughts.for.today




# -------------------------------------------




echo "path_name = $path_name"




t=${path_name##/*/}




echo "path_name, stripped of prefixes = $t"




# Same effect as   t=`basename $path_name` in this particular case.




#  t=${path_name%/}; t=${t##*/}   is a more general solution,




#+ but still fails sometimes.




#  If $path_name ends with a newline, then `basename $path_name` will not work,




#+ but the above expression will.




# (Thanks, S.C.)









t=${path_name%/*.*}




# Same effect as   t=`dirname $path_name`




echo "path_name, stripped of suffixes = $t"




# These will fail in some cases, such as "../", "/foo////", # "foo/", "/".




#  Removing suffixes, especially when the basename has no suffix,




#+ but the dirname does, also complicates matters.




# (Thanks, S.C.)









echo









t=${path_name:11}




echo "$path_name, with first 11 chars stripped off = $t"




t=${path_name:11:5}




echo "$path_name, with first 11 chars stripped off, length 5 = $t"









echo









t=${path_name/bozo/clown}




echo "$path_name with \"bozo\" replaced  by \"clown\" = $t"




t=${path_name/today/}




echo "$path_name with \"today\" deleted = $t"




t=${path_name//o/O}




echo "$path_name with all o's capitalized = $t"




t=${path_name//o/}




echo "$path_name with all o's deleted = $t"









exit 0
${var/#Pattern/Replacement}

If prefix of var matches Pattern, then substitute Replacement for Pattern.

${var/%Pattern/Replacement}

If suffix of var matches Pattern, then substitute Replacement for Pattern.

Example 9-19. Matching patterns at prefix or suffix of string

#!/bin/bash




# Pattern replacement at prefix / suffix of string.









v0=abc1234zip1234abc    # Original variable.




echo "v0 = $v0"         # abc1234zip1234abc




echo









# Match at prefix (beginning) of string.




v1=${v0/#abc/ABCDEF}    # abc1234zip1234abc




                        # |-|




echo "v1 = $v1"         # ABCDE1234zip1234abc




                        # |---|









# Match at suffix (end) of string.




v2=${v0/%abc/ABCDEF}    # abc1234zip123abc




                        #              |-|




echo "v2 = $v2"         # abc1234zip1234ABCDEF




                        #               |----|









echo









#  ----------------------------------------------------




#  Must match at beginning / end of string,




#+ otherwise no replacement results.




#  ----------------------------------------------------




v3=${v0/#123/000}       # Matches, but not at beginning.




echo "v3 = $v3"         # abc1234zip1234abc




                        # NO REPLACEMENT.




v4=${v0/%123/000}       # Matches, but not at end.




echo "v4 = $v4"         # abc1234zip1234abc




                        # NO REPLACEMENT.









exit 0			
${!varprefix*}, ${!varprefix@}

Matches all previously declared variables beginning with varprefix.
xyz23=whatever




xyz24=









a=${!xyz*}      # Expands to names of declared variables beginning with "xyz".




echo "a = $a"   # a = xyz23 xyz24




a=${!xyz@}      # Same as above.




echo "a = $a"   # a = xyz23 xyz24









# Bash, version 2.04, adds this feature.

Notes

[1]

If $parameter is null in a non-interactive script, it will terminate with a 127 exit status (the Bash error code code for "command not found").