Parameter expansion
Parameter expansion retrieves the value of a parameter when a command is executed. The basic syntax is ${parameter}.
$ user="Alice" # define a variable
$ echo "Hello, ${user}!" # expand the variable
Hello, Alice!
Unset parameters
If a parameter is unset, the shell expands it to an empty string by default.
$ unset user
$ echo "Hello, ${user}!"
Hello, !
If the nounset shell option is enabled, expanding an unset parameter is an error:
$ set -o nounset
$ echo "Hello, ${user}!"
error: cannot expand unset parameter
--> <stdin>:2:14
|
2 | echo "Hello, ${user}!"
| ^^^^^^^ parameter `user` is not set
|
= note: unset parameters are disallowed by the nounset option
Using nounset is recommended to catch typos in variable names.
Omitting braces
Braces are optional if the parameter is:
- a variable name with only ASCII letters, digits, and underscores (e.g.,
$HOME,$user) - a special parameter (e.g.,
$?,$#) - a single-digit positional parameter (e.g.,
$1,$2)
For variable names, the shell uses the longest possible name after $, regardless of whether the variable exists:
$ user="Alice"
$ unset username
$ echo "Hello, $username!" # $user is not considered
Hello, !
For positional parameters, only a single digit is used, even if followed by more digits:
$ set foo bar baz # set three positional parameters
$ echo "$12" # $1 expands to the first positional parameter
foo2
Modifiers
Modifiers change the value of a parameter during expansion. Modifiers can only be used in braced expansions, and only one modifier is allowed per expansion.
Length
The length modifier ${#parameter} returns the number of characters in the parameter’s value.
$ user="Alice"
$ echo "Length of user: ${#user}"
Length of user: 5
As an extension, the length modifier can be used with arrays or special parameters * or @, applying the modifier to each element:
$ users=(Alice Bob Charlie)
$ echo "Lengths of users: ${#users}"
Lengths of users: 5 3 7
$ set yellow red green blue # set four positional parameters
$ echo "Lengths of positional parameters: ${#*}"
Lengths of positional parameters: 6 3 5 4
Switch
The switch modifier changes the result based on whether a parameter is set or empty. There are eight forms:
${parameter-word}– Usewordifparameteris unset.${parameter:-word}– Usewordifparameteris unset or empty.${parameter+word}– Usewordifparameteris set.${parameter:+word}– Usewordifparameteris set and not empty.${parameter=word}– Assignwordtoparameterif unset, using the new value.${parameter:=word}– Assignwordtoparameterif unset or empty, using the new value.${parameter?word}– Error withwordifparameteris unset.${parameter:?word}– Error withwordifparameteris unset or empty.
Examples:
$ user="Alice"
$ echo "Hello, ${user-World}!"
Hello, Alice!
$ unset user
$ echo "Hello, ${user-World}!"
Hello, World!
$ unset PATH
$ PATH="/bin${PATH:+:$PATH}"
$ echo "$PATH"
/bin
$ PATH="/usr/bin${PATH:+:$PATH}"
$ echo "$PATH"
/usr/bin:/bin
$ unset user
$ echo "Hello, ${user=Alice}!"
Hello, Alice!
$ echo "Hello, ${user=Bob}!"
Hello, Alice!
$ user="Alice"
$ echo "Hello, ${user?tell me your name}!"
Hello, Alice!
$ unset user
$ echo "Hello, ${user?tell me your name}!"
error: tell me your name
--> <stdin>:4:14
|
4 | echo "Hello, ${user?tell me your name}!"
| ^^^^^^^^^^^^^^^^^^^^^^^^^ parameter `user` is not set
In all cases, the following expansions are performed on word before use:
- Tilde expansion (unless the parameter expansion is in double quotes)
- Parameter expansion (recursive!)
- Command substitution
- Arithmetic expansion
For the = and := forms, quote removal is also performed before assignment. Assignment only works for variables, not special or positional parameters.
If word is empty in the ? and :? forms, a default error message is used.
The nounset option does not apply to expansions with a switch modifier.
Trim
The trim modifier removes leading or trailing characters matching a pattern from a parameter’s value. There are four forms:
${parameter#pattern}– Remove the shortest match ofpatternfrom the start.${parameter##pattern}– Remove the longest match ofpatternfrom the start.${parameter%pattern}– Remove the shortest match ofpatternfrom the end.${parameter%%pattern}– Remove the longest match ofpatternfrom the end.
The value is matched against the pattern, and the matching part is removed.
$ var="banana"
$ echo "${var#*a}"
nana
$ echo "${var##*a}"
$ echo "${var%a*}"
banan
$ echo "${var%%a*}"
b
The pattern is expanded before use:
- Tilde expansion
- Parameter expansion (recursive!)
- Command substitution
- Arithmetic expansion
You can quote part or all of the pattern to treat it literally:
$ asterisks="***"
$ echo "${asterisks##*}" # removes the whole value
$ echo "${asterisks##\*}" # removes the first *
**
$ echo "${asterisks##'**'}" # removes the first two *
*
Compatibility
Some modifiers are ambiguous when used with a certain special parameter. Yash and many other shells interpret ${##}, ${#-}, and ${#?} as length modifiers applied to special parameters #, -, and ?, not as switch or trim modifiers applied to #. The POSIX standard is unclear on this point.
The result is unspecified in POSIX for:
- a length or switch modifier applied to special parameter
*or@ - a trim modifier applied to special parameter
#,*, or@