One core functionality of Bash is to manage **parameters**. A parameter is an entity that stores values and is referenced by a **name**, a **number** or a **special symbol**.
* parameters referenced by a name are called **variables** (this also applies to [[syntax:arrays|arrays]])
* parameters referenced by a number are called **positional parameters** and reflect the arguments given to a shell
* parameters referenced by a **special symbol** are auto-set parameters that have different special meanings and uses
**Parameter expansion** is the procedure to get the value from the referenced entity, like expanding a variable to print its value. On expansion time you can do very nasty things with the parameter or its value. These things are described here.
**If you saw** some parameter expansion syntax somewhere, and need to check what it can be, try the overview section below!
**Arrays** can be special cases for parameter expansion, every applicable description mentions arrays below. Please also see the [[syntax:arrays|article about arrays]].
For a more technical view what a parameter is and which types exist, [[dict:terms:parameter | see the dictionary entry for "parameter"]].
===== Overview =====
Looking for a specific syntax you saw, without knowing the name?
* [[#simple_usage | Simple usage]]
* ''$PARAMETER''
* ''${PARAMETER}''
* [[#indirection | Indirection]]
* ''${!PARAMETER}''
* [[#case_modification | Case modification ]]
* ''${PARAMETER^}''
* ''${PARAMETER^^}''
* ''${PARAMETER,}''
* ''${PARAMETER,,}''
* ''${PARAMETER~}''
* ''${PARAMETER~~}''
* [[#variable_name_expansion | Variable name expansion]]
* ''${!PREFIX*}''
* ''${!PREFIX@}''
* [[#substring_removal | Substring removal]] (also for **filename manipulation**!)
* [[#assign_a_default_value | Assign a default value]]
* ''${PARAMETER:=WORD}''
* ''${PARAMETER=WORD}''
* [[#use_an_alternate_value | Use an alternate value]]
* ''${PARAMETER:+WORD}''
* ''${PARAMETER+WORD}''
* [[#display_error_if_null_or_unset | Display error if null or unset]]
* ''${PARAMETER:?WORD}''
* ''${PARAMETER?WORD}''
===== Simple usage =====
''$PARAMETER''
''${PARAMETER}''
The easiest form is to just use a parameter's name within braces. This is identical to using ''$FOO'' like you see it everywhere, but has the advantage that it can be immediately followed by characters that would be interpreted as part of the parameter name otherwise. Compare these two expressions (''WORD="car"'' for example), where we want to print a word with a trailing "s":
__Why does the first one fail?__ It prints nothing, because a parameter (variable) named "''WORDs''" is undefined and thus printed as "" (//nothing//). Without using braces for parameter expansion, Bash will interpret the sequence of all valid characters from the introducing "''$''" up to the last valid character as name of the parameter. When using braces you just force Bash to **only interpret the name inside your braces**.
Also, please remember, that **parameter names are** (like nearly everything in UNIX(r)) **case sensitive!**
The second form with the curly braces is also needed to access positional parameters (arguments to a script) beyond ''$9'':
See also the [[syntax:arrays|article about general array syntax]]
For arrays you always need the braces. The arrays are expanded by individual indexes or mass arguments. An individual index behaves like a normal parameter, for the mass expansion, please read the article about arrays linked above.
to enter a level of indirection. The referenced parameter is not ''PARAMETER'' itself, but the parameter whose name is stored as the value of ''PARAMETER''. If the parameter ''PARAMETER'' has the value "''TEMP''", then ''${!PARAMETER}'' will expand to the value of the parameter named ''TEMP'':
You can think of this mechanism as being roughly equivalent to taking any parameter expansion that begins with the parameter name, and substituting the ''!PARAMETER'' part with the value of PARAMETER.
It was an unfortunate design decision to use the ''!'' prefix for indirection, as it introduces parsing ambiguity with other parameter expansions that begin with ''!''. Indirection is not possible in combination with any parameter expansion whose modifier requires a prefix to the parameter name. Specifically, indirection isn't possible on the ''${!var@}'', ''${!var*}'', ''${!var[@]}'', ''${!var[*]}'', and ''${#var}'' forms. This means the ''!'' prefix can't be used to retrieve the indices of an array, the length of a string, or number of elements in an array indirectly (see [[syntax/arrays#indirection]] for workarounds). Additionally, the ''!''-prefixed parameter expansion conflicts with ksh-like shells which have the more powerful "name-reference" form of indirection, where the exact same syntax is used to expand to the name of the variable being referenced.
Indirect references to [[syntax:arrays|array names]] are also possible since the Bash 3 series (exact version unknown), but undocumented. See [[syntax/arrays#indirection]] for details.
Chet has added an initial implementation of the ksh ''nameref'' declaration command to the git devel branch. (''declare -n'', ''local -n'', etc, will be supported). This will finally address many issues around passing and returning complex datatypes to/from functions.
===== Case modification =====
''${PARAMETER^}''
''${PARAMETER^^}''
''${PARAMETER,}''
''${PARAMETER,,}''
''${PARAMETER~}''
''${PARAMETER~~}''
These expansion operators modify the case of the letters in the expanded text.
The ''^'' operator modifies the first character to uppercase, the '','' operator to lowercase. When using the double-form (''^^'' and '',,''), all characters are converted.
The (**currently undocumented**) operators ''~'' and ''~~'' reverse the case of the given text (in ''PARAMETER'').''~'' reverses the case of first letter of words in the variable while ''~~'' reverses case for all. Thanks to ''Bushmills'' and ''geirha'' on the Freenode IRC channel for this finding.
__**Note:**__ Case modification is a handy feature you can apply to a name or a title. Or is it? Case modification was an important aspect of the Bash 4 release. Bash version 4, RC1 would perform word splitting, and then case modification, resulting in title case (where every word is capitalized). It was decided to apply case modification to values, not words, for the Bash 4 release. Thanks Chet.
==== Case modification: Arrays ====
Case modification can be used to create the proper capitalization for names or titles. Just assign it to an array:
''declare -a title=(my hello world john smith)''
For [[syntax:arrays|array]] expansion, the case modification applies to **every expanded element, no matter if you expand an individual index or mass-expand** the whole array using ''@'' or ''*'' subscripts. Some examples:
This expands to a list of all set **variable names** beginning with the string ''PREFIX''. The elements of the list are separated by the first character in the ''IFS''-variable (<space> by default).
This one can **expand only a part** of a parameter's value, **given a pattern to describe what to remove** from the string. The pattern is interpreted just like a pattern to describe a filename to match (globbing). See [[syntax:pattern | Pattern matching]] for more.
The operator "''#''" will try to remove the shortest text matching the pattern, while "''##''" tries to do it with the longest text matching. Look at the following examples to get the idea (matched text <del>marked striked</del>, remember it will be removed!):
These are the syntaxes for filenames with a single extension. Depending on your needs, you might need to adjust shortest/longest match.
==== Substring removal: Arrays ====
As for most parameter expansion features, working on [[syntax:arrays|arrays]] **will handle each expanded element**, for individual expansion and also for mass expansion.
Simple example, removing a trailing ''is'' from all array elements (on expansion):
This one can substitute (//replace//) a substring [[syntax:pattern | matched by a pattern]], on expansion time. The matched substring will be entirely removed and the given string will be inserted. Again some example string for the tests:
The two main forms only differ in **the number of slashes** after the parameter name: ''${PARAMETER/PATTERN/STRING}'' and ''<nowiki>${PARAMETER//PATTERN/STRING}</nowiki>''
The first one (//one slash//) is to only substitute **the first occurrence** of the given pattern, the second one (//two slashes//) is to substitute **all occurrences** of the pattern.
First, let's try to say "happy" instead of "conservative" in our example string:
Additionally you can "anchor" an expression:
A ''#'' (hashmark) will indicate that your expression is matched against the beginning portion of the string, a ''%'' (percent-sign) will do it for the end portion.
If the replacement part is completely omitted, the matches are replaced by the nullstring, i.e., they are removed. This is equivalent to specifying an empty replacement:
This parameter expansion type applied to [[syntax:arrays|arrays]] **applies to all expanded elements**, no matter if an individual element is expanded, or all elements using the mass expansion syntaxes.
A simple example, changing the (lowercase) letter ''t'' to ''d'':
The length is reported in characters, not in bytes. Depending on your environment this may not always be the same (multibyte-characters, like in UTF8 encoding).
There's not much to say about it, mh?
==== (String) length: Arrays ====
For [[syntax:arrays|arrays]], this expansion type has two meanings:
* For **individual** elements, it reports the string length of the element (as for every "normal" parameter)
* For the **mass subscripts** ''@'' and ''*'' it reports the number of set elements in the array
__**Attention:**__ The number of used elements does not need to conform to the highest index. Sparse arrays are possible in Bash, that means you can have 4 elements, but with indexes 1, 7, 20, 31. **You can't loop through such an array with a counter loop based on the number of elements!**
===== Substring expansion =====
''${PARAMETER:OFFSET}''
''${PARAMETER:OFFSET:LENGTH}''
This one can expand only a **part** of a parameter's value, given a **position to start** and maybe a **length**. If ''LENGTH'' is omitted, the parameter will be expanded up to the end of the string. If ''LENGTH'' is negative, it's taken as a second offset into the string, counting from the end of the string.
''OFFSET'' and ''LENGTH'' can be **any** [[syntax:arith_expr | arithmetic expression]]. **Take care:** The ''OFFSET'' starts at 0, not at 1!
Example string (a quote from a big man):
''MYSTRING="Be liberal in what you accept, and conservative in what you send"''
==== Using only Offset ====
In the first form, the expansion is used without a length value, note that the offset 0 is the first character:
If the given offset is negative, it's counted from the end of the string, i.e. an offset of -1 is the last character. In that case, the length still counts forward, of course. One special thing is to do when using a negative offset: You need to separate the (negative) number from the colon:
This works since Bash 4.2-alpha, see also [[scripting:bashchanges]].
==== Substring/Element expansion: Arrays ====
For [[syntax:arrays|arrays]], this expansion type has again 2 meanings:
* For **individual** elements, it expands to the specified substring (as for every “normal” parameter)
* For the **mass subscripts** ''@'' and ''*'' it mass-expands individual array elements denoted by the 2 numbers given (//starting element//, //number of elements//)
If the parameter ''PARAMETER'' is unset (never was defined) or null (empty), this one expands to ''WORD'', otherwise it expands to the value of ''PARAMETER'', as if it just was ''${PARAMETER}''. If you omit the '':'' (colon), like shown in the second form, the default value is only used when the parameter was **unset**, not when it was empty.
It will print "Your gender is a secret." when you don't enter the gender. Note that the default value is **used on expansion time**, it is **not assigned to the parameter**.
==== Use a default value: Arrays ====
For [[syntax:arrays|arrays]], the behaviour is very similar. Again, you have to make a difference between expanding an individual element by a given index and mass-expanding the array using the ''@'' and ''*'' subscripts.
* For individual elements, it's the very same: If the expanded element is ''NULL'' or unset (watch the '':-'' and ''-'' variants), the default text is expanded
* For mass-expansion syntax, the default text is expanded if the array
* contains no element or is unset (the '':-'' and ''-'' variants mean the **same** here)
* contains only elements that are the nullstring (the '':-'' variant)
In other words: The basic meaning of this expansion type is applied as consistent as possible to arrays.
Example code (please try the example cases yourself):
This one works like the [[syntax:pe#use_a_default_value | using default values]], but the default text you give is not only expanded, but also **assigned** to the parameter, if it was unset or null. Equivalent to using a default value, when you omit the '':'' (colon), as shown in the second form, the default value will only be assigned when the parameter was **unset**.
For [[syntax:arrays|arrays]] this expansion type is limited. For an individual index, it behaves like for a "normal" parameter, the default value is assigned to this one element. The mass-expansion subscripts ''@'' and ''*'' **can not be used here** because it's not possible to assign to them!
===== Use an alternate value =====
''${PARAMETER:+WORD}''
''${PARAMETER+WORD}''
This form expands to nothing if the parameter is unset or empty. If it is set, it does not expand to the parameter's value, **but to some text you can specify**:
If you omit the colon, as shown in the second form (''${PARAMETER+WORD}''), the alternate value will be used if the parameter is set (and it can be empty)! You can use it, for example, to complain if variables you need (and that can be empty) are undefined:
Similar to the cases for [[syntax:arrays|arrays]] to expand to a default value, this expansion behaves like for a "normal" parameter when using individual array elements by index, but reacts differently when using the mass-expansion subscripts ''@'' and ''*'':
* For individual elements, it's the very same: If the expanded element is **not** NULL or unset (watch the :+ and + variants), the alternate text is expanded
* For mass-expansion syntax, the alternate text is expanded if the array
* contains elements where min. one element is **not** a nullstring (the :+ and + variants mean the same here)
* contains **only** elements that are **not** the nullstring (the :+ variant)
For some cases to play with, please see the code examples in the [[#use_a_default_valuearrays|description for using a default value]].
===== Display error if null or unset =====
''${PARAMETER:?WORD}''
''${PARAMETER?WORD}''
If the parameter ''PARAMETER'' is set/non-null, this form will simply expand it. Otherwise, the expansion of ''WORD'' will be used as appendix for an error message:
* **Fixed in 4.2.36** ([[ftp://ftp.cwru.edu/pub/bash/bash-4.2-patches/bash42-036 | patch]]). Bash doesn't follow either POSIX or its own documentation when expanding either a quoted ''"$@"'' or ''"${arr[@]}"'' with an adjacent expansion. ''"$@$x"'' expands in the same way as ''"$*$x"'' - i.e. all parameters plus the adjacent expansion are concatenated into a single argument. As a workaround, each expansion needs to be quoted separately. Unfortunately, this bug took a very long time to notice.<code>
~ $ set -- a b c; x=foo; printf '<%s> ' "$@$x""$*""$x""$@""$x"
* Almost all shells disagree about the treatment of an unquoted ''$@'', ''${arr[@]}'', ''$*'', and ''${arr[*]}'' when [[http://mywiki.wooledge.org/IFS | IFS]] is set to null. POSIX is unclear about the expected behavior. A null IFS causes both [[syntax:expansion:wordsplit | word splitting]] and [[syntax:expansion:globs | pathname expansion]] to behave randomly. Since there are few good reasons to leave ''IFS'' set to null for more than the duration of a command or two, and even fewer to expand ''$@'' and ''$*'' unquoted, this should be a rare issue. **Always quote them**!<code>
</code>When ''IFS'' is set to a non-null value, or unset, all shells behave the same - first expanding into separate args, then applying pathname expansion and word-splitting to the results, except for zsh, which doesn't do pathname expansion in its default mode.
* Additionally, shells disagree about various wordsplitting behaviors, the behavior of inserting delimiter characters from IFS in ''$*'', and the way adjacent arguments are concatenated, when IFS is modified in the middle of expansion through side-effects.<code>
</code>ksh93 and mksh can additionally achieve this side effect (and others) via the ''${ cmds;}'' expansion. I haven't yet tested every possible side-effect that can affect expansion halfway through expansion that way.
* As previously mentioned, the Bash form of indirection by prefixing a parameter expansion with a ''!'' conflicts with the same syntax used by mksh, zsh, and ksh93 for a different purpose. Bash will "slightly" modify this expansion in the next version with the addition of namerefs.
* Bash (and most other shells) don't allow .'s in identifiers. In ksh93, dots in variable names are used to reference methods (i.e. "Discipline Functions"), attributes, special shell variables, and to define the "real value" of an instance of a class.
* In ksh93, the ''_'' parameter has even more uses. It is used in the same way as ''self'' in some object-oriented languages; as a placeholder for some data local to a class; and also as the mechanism for class inheritance. In most other contexts, ''_'' is compatible with Bash.
* Bash only evaluates the subscripts of the slice expansion (''${x:y:z}'') if the parameter is set (for both nested expansions and arithmetic). For ranges, Bash evaluates as little as possible, i.e., if the first part is out of range, the second won't be evaluated. ksh93 and mksh always evaluate the subscript parts even if the parameter is unset. <code>
$ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=(); echo "${x[@]:n,6:m}"' # No output
$ bash -c 'n="y[\$(printf yo >&2)1]" m="y[\$(printf jo >&2)1]"; x=([5]=hi); echo "${x[@]:n,6:m}"'
* In most shells, when dealing with an "alternate" parameter expansion that expands to multiple words, and nesting such expansions, not all combinations of nested quoting are possible.
$ printf "<%s> ""${b[@]-"${a[@]}""${a[@]}"}"; echo # The entire PE is quoted so Bash considers the inner quotes redundant.
<meh><bleh><blergmeh><bleh><blerg>
$ printf "<%s> ""${b[@]-${a[@]} ${a[@]}}"; echo # The outer quotes cause the inner expansions to be considered quoted.
This all might be intuitive, and is the most common implementation, but this design sucks for a number of reasons. For one, it means Bash makes it absolutely impossible to expand any part of the inner region //unquoted// while leaving the outer region quoted. Quoting the outer forces quoting of the inner regions recursively (except nested command substitutions of course). Word-splitting is necessary to split words of the inner region, which cannot be done together with outer quoting. Consider the following (only slightly far-fetched) code:
This final line is perhaps not the most obvious, but I've run into cases were this type of logic can be desirable and realistic. We can deduce what was intended:
* If ''someCmd'' is set, then the resulting expansion should run the command: ''"myCmd""arg1""arg2 yay!""third*arg*""4""arg5"''
* Otherwise, if ''someCmd'' is not set, expand ''$someOtherCmd'' and the inner args, to run a different command: ''"mycommand""arg2""arg3""arg4""arg5"''.
Unfortunately, it is impossible to get the intended result in Bash (and most other shells) without taking a considerably different approach. The only way to split the literal inner parts is through word-splitting, which requires that the PE be unquoted. But, the only way to expand the outer expansion correctly without word-splitting or globbing is to quote it. Bash will actually expand the command as one of these:
To the best of my knowledge, ksh93 is the only shell that acts differently. Rather than forcing nested expansions into quoting, a quote at the beginning and end of the nested region will cause the quote state to reverse itself within the nested part. I have no idea whether it's an intentional or documented effect, but it does solve the problem and consequently adds a lot of potential power to these expansions.
This can be used to control the quote state of any part of any expansion to an arbitrary depth. Sadly, it is the only shell that does this and the difference may introduce a possible compatibility problem.
===== See also =====
* Internal: [[syntax:expansion:intro | Introduction to expansion and substitution]]