Bash Parameter Expansion
The $ character introduces parameter expansion, command substitution, or arithmetic expansion. The parameter name or symbol to be expanded may be enclosed in braces, which are optional but serve to protect the variable to be expanded from characters immediately following it which could be interpreted as part of the name.
Read more in the Bash User Manual.
Modifying the case of alphabetic characters
Section titled “Modifying the case of alphabetic characters”To uppercase
$ v="hello"# Just the first character$ printf '%s\n' "${v^}"Hello# All characters$ printf '%s\n' "${v^^}"HELLO# Alternative$ v="hello world"$ declare -u string="$v"$ echo "$string"HELLO WORLDTo lowercase
$ v="BYE"# Just the first character$ printf '%s\n' "${v,}"bYE# All characters$ printf '%s\n' "${v,,}"bye# Alternative$ v="HELLO WORLD"$ declare -l string="$v"$ echo "$string"hello worldToggle Case
$ v="Hello World"# All chars$ echo "${v~~}"hELLO wORLD$ echo "${v~}"# Just the first charhello WorldLength of parameter
Section titled “Length of parameter”# Length of a string$ var='12345'$ echo "${#var}"5Note that it’s the length in number of characters which is not necessarily the same as the number of bytes (like in UTF-8 where most characters are encoded in more than one byte), nor the number of glyphs/graphemes (some of which are combinations of characters), nor is it necessarily the same as the display width.
# Number of array elements$ myarr=(1 2 3)$ echo "${#myarr[@]}"3
# Works for positional parameters as well$ set -- 1 2 3 4$ echo "${#@}"4
# But more commonly (and portably to other shells), one would use$ echo "$#"4Replace pattern in string
Section titled “Replace pattern in string”First match:
$ a='I am a string'$ echo "${a/a/A}"I Am a stringAll matches:
$ echo "${a//a/A}"I Am A stringMatch at the beginning:
$ echo "${a/#I/y}"y am a stringMatch at the end:
$ echo "${a/%g/N}"I am a strinNReplace a pattern with nothing:
$ echo "${a/g/}"I am a strinAdd prefix to array items:
$ A=(hello world)$ echo "${A[@]/#/R}"Rhello RworldSubstrings and subarrays
Section titled “Substrings and subarrays”var='0123456789abcdef'
# Define a zero-based offset$ printf '%s\n' "${var:3}"3456789abcdef
# Offset and length of substring$ printf '%s\n' "${var:3:4}"3456# Negative length counts from the end of the string$ printf '%s\n' "${var:3:-5}"3456789a
# Negative offset counts from the end# Needs a space to avoid confusion with ${var:-6}$ printf '%s\n' "${var: -6}"abcdef
# Alternative: parentheses$ printf '%s\n' "${var:(-6)}"abcdef
# Negative offset and negative length$ printf '%s\n' "${var: -6:-5}"aThe same expansions apply if the parameter is a positional parameter or the element of a subscripted array:
# Set positional parameter $1set -- 0123456789abcdef
# Define offset$ printf '%s\n' "${1:5}"56789abcdef
# Assign to array elementmyarr[0]='0123456789abcdef'
# Define offset and length$ printf '%s\n' "${myarr[0]:7:3}"789Analogous expansions apply to positional parameters, where offsets are one-based:
# Set positional parameters $1, $2, ...$ set -- 1 2 3 4 5 6 7 8 9 0 a b c d e f
# Define an offset (beware $0 (not a positional parameter)# is being considered here as well)$ printf '%s\n' "${@:10}"0abcdef
# Define an offset and a length$ printf '%s\n' "${@:10:3}"0ab
# No negative lengths allowed for positional parameters$ printf '%s\n' "${@:10:-2}"bash: -2: substring expression < 0
# Negative offset counts from the end# Needs a space to avoid confusion with ${@:-10:2}$ printf '%s\n' "${@: -10:2}"78
# ${@:0} is $0 which is not otherwise a positional parameters or part# of $@$ printf '%s\n' "${@:0:2}"/usr/bin/bash1Substring expansion can be used with indexed arrays:
# Create array (zero-based indices)$ myarr=(0 1 2 3 4 5 6 7 8 9 a b c d e f)
# Elements with index 5 and higher$ printf '%s\n' "${myarr[@]:12}"cdef
# 3 elements, starting with index 5$ printf '%s\n' "${myarr[@]:5:3}"567
# The last element of the array$ printf '%s\n' "${myarr[@]: -1}"fDelete a pattern from the beginning of a string
Section titled “Delete a pattern from the beginning of a string”Shortest match:
$ a='I am a string'$ echo "${a#*a}"m a stringLongest match:
$ echo "${a##*a}" stringParameter indirection
Section titled “Parameter indirection”Bash indirection permits to get the value of a variable whose name is contained in another variable. Variables example:
$ red="the color red"$ green="the color green"
$ color=red$ echo "${!color}"the color red$ color=green$ echo "${!color}"the color greenSome more examples that demonstrate the indirect expansion usage:
$ foo=10 $ x=foo $ echo ${x} #Classic variable print foo
$ foo=10 $ x=foo $ echo ${!x} #Indirect expansion 10One more example:
$ argtester () { for (( i=1; i<="$#"; i++ )); do echo "${i}";done; }; argtester -ab -cd -ef1 #i expanded to 12 #i expanded to 23 #i expanded to 3
$ argtester () { for (( i=1; i<="$#"; i++ )); do echo "${!i}";done; }; argtester -ab -cd -ef-ab # i=1 --> expanded to $1 ---> expanded to first argument sent to function-cd # i=2 --> expanded to $2 ---> expanded to second argument sent to function-ef # i=3 --> expanded to $3 ---> expanded to third argument sent to functionDefault value substitution
Section titled “Default value substitution”${parameter:-word}If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted.
$ unset var$ echo "${var:-XX}" # Parameter is unset -> expansion XX occursXX$ var="" # Parameter is null -> expansion XX occurs$ echo "${var:-XX}"XX$ var=23 # Parameter is not null -> original expansion occurs$ echo "${var:-XX}"23${parameter:=word}If parameter is unset or null, the expansion of word is assigned to parameter. The value of parameter is then substituted. Positional parameters and special parameters may not be assigned to in this way.
$ unset var$ echo "${var:=XX}" # Parameter is unset -> word is assigned to XXXX$ echo "$var"XX$ var="" # Parameter is null -> word is assigned to XX$ echo "${var:=XX}"XX$ echo "$var"XX$ var=23 # Parameter is not null -> no assignment occurs$ echo "${var:=XX}"23$ echo "$var"23Delete a pattern from the end of a string
Section titled “Delete a pattern from the end of a string”Shortest match:
$ a='I am a string'$ echo "${a%a*}"I amLongest match:
$ echo "${a%%a*}"IParameter expansion and filenames
Section titled “Parameter expansion and filenames”You can use Bash Parameter Expansion to emulate common filename-processing operations like basename and dirname.
We will use this as our example path:
FILENAME="/tmp/example/myfile.txt"To emulate dirname and return the directory name of a file path:
echo "${FILENAME%/*}"To emulate basename $FILENAME and return the filename of a file path:
echo "${FILENAME##*/}"#Out: myfile.txtTo emulate basename $FILENAME .txt and return the filename without the .txt. extension:
BASENAME="${FILENAME##*/}"echo "${BASENAME%%.txt}"#Out: myfileError if variable is empty or unset
Section titled “Error if variable is empty or unset”The semantics for this are similar to that of default value substitution, but instead of substituting a default value, it errors out with the provided error message. The forms are ${VARNAME?ERRMSG} and ${VARNAME:?ERRMSG}. The form with : will error our if the variable is unset or empty, whereas the form without will only error out if the variable is unset. If an error is thrown, the ERRMSG is output and the exit code is set to 1.
#!/bin/bashFOO=# ./script.sh: line 4: FOO: EMPTYecho "FOO is ${FOO:?EMPTY}"# FOO isecho "FOO is ${FOO?UNSET}"# ./script.sh: line 8: BAR: EMPTYecho "BAR is ${BAR:?EMPTY}"# ./script.sh: line 10: BAR: UNSETecho "BAR is ${BAR?UNSET}"The run the full example above each of the erroring echo statements needs to be commented out to proceed.
Munging during expansion
Section titled “Munging during expansion”Variables don’t necessarily have to expand to their values - substrings can be extracted during expansion, which can be useful for extracting file extensions or parts of paths. Globbing characters keep their usual meanings, so .* refers to a literal dot, followed by any sequence of characters; it’s not a regular expression.
$ v=foo-bar-baz$ echo ${v%%-*}foo$ echo ${v%-*}foo-bar$ echo ${v##*-}baz$ echo ${v#*-}bar-bazIt’s also possible to expand a variable using a default value - say I want to invoke the user’s editor, but if they’ve not set one I’d like to give them vim.
$ EDITOR=nano$ ${EDITOR:-vim} /tmp/some_file# opens nano$ unset EDITOR$ $ ${EDITOR:-vim} /tmp/some_file# opens vimThere are two different ways of performing this expansion, which differ in whether the relevant variable is empty or unset. Using :- will use the default if the variable is either unset or empty, whilst - only uses the default if the variable is unset, but will use the variable if it is set to the empty string:
$ a="set"$ b=""$ unset c$ echo ${a:-default_a} ${b:-default_b} ${c:-default_c}set default_b default_c$ echo ${a-default_a} ${b-default_b} ${c-default_c}set default_cSimilar to defaults, alternatives can be given; where a default is used if a particular variable isn’t available, an alternative is used if the variable is available.
$ a="set"$ b=""$ echo ${a:+alternative_a} ${b:+alternative_b}alternative_aNoting that these expansions can be nested, using alternatives becomes particularly useful when supplying arguments to command line flags;
$ output_file=/tmp/foo$ wget ${output_file:+"-o ${output_file}"} www.stackexchange.com# expands to wget -o /tmp/foo www.stackexchange.com$ unset output_file$ wget ${output_file:+"-o ${output_file}"} www.stackexchange.com# expands to wget www.stackexchange.comSyntax
Section titled “Syntax”- ${parameter:offset} # Substring starting at offset
- ${parameter:offset:length} # Substring of length “length” starting at offset
- ${#parameter} # Length of parameter
- ${parameter/pattern/string} # Replace the first occurrence of pattern with string
- ${parameter//pattern/string} # Replace all occurrences of pattern with string
- ${parameter/#pattern/string} # Replace pattern with string if pattern is at the beginning
- ${parameter/%pattern/string} # Replace pattern with string if pattern is at the ending
- ${parameter#pattern} # Remove shortest match of pattern from beginning of parameter
- ${parameter##pattern} # Remove longest match of pattern from beginning of parameter
- ${parameter%pattern} # Remove shortest match of pattern from end of parameter
- ${parameter%%pattern} # Remove longest match of pattern from end of parameter
- ${parameter:-word} # Expand to word if parameter unset/undefined
- ${parameter:=word} # Expand to word if parameter unset/undefined and set parameter
- ${parameter:+word} # Expand to word if parameter set/defined