Skip to main content
Engineering LibreTexts

13-C.7: Shell Expansion / Variable Substitution / Command Substitution

  • Page ID
    43196
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    Shell Expansion

    After the command has been split into tokens, these tokens or words are expanded or resolved. There are eight kinds of expansion performed, which we will discuss in the next sections, in the order that they are expanded.

    After all expansions, quote removal is performed.

    Brace Expansion

    Brace expansion is a mechanism by which arbitrary strings may be generated. Patterns to be brace-expanded take the form of an optional PREAMBLE, followed by a series of comma-separated strings between a pair of braces, followed by an optional POSTSCRIPT. The preamble is prefixed to each string contained within the braces, and the postscript is then appended to each resulting string, expanding left to right.

    Brace expansions may be nested. The results of each expanded string are not sorted; left to right order is preserved:

    pbmac@pbmac-server $ echo sp{el,il,al}l
    spell spill spall
    

    Brace expansion is performed before any other expansions, and any characters special to other expansions are preserved in the result. It is strictly textual. Bash does not apply any syntactic interpretation to the context of the expansion or the text between the braces. To avoid conflicts with parameter expansion, the string "${" is not considered eligible for brace expansion.

    A correctly-formed brace expansion must contain unquoted opening and closing braces, and at least one unquoted comma. Any incorrectly formed brace expansion is left unchanged.

    Tilde Expansion

    If a word begins with an unquoted tilde character ("~"), all of the characters up to the first unquoted slash (or all characters, if there is no unquoted slash) are considered a tilde-prefix. If none of the characters in the tilde-prefix are quoted, the characters in the tilde-prefix following the tilde are treated as a possible login name. If this login name is the null string, the tilde is replaced with the value of the HOME shell variable. If HOME is unset, the home directory of the user executing the shell is substituted instead. Otherwise, the tilde-prefix is replaced with the home directory associated with the specified login name.

    If the tilde-prefix is "~+", the value of the shell variable PWD replaces the tilde-prefix. If the tilde-prefix is "~-", the value of the shell variable OLDPWD , if it is set, is substituted.

    If the characters following the tilde in the tilde-prefix consist of a number N, optionally prefixed by a "+" or a "-", the tilde-prefix is replaced with the corresponding element from the directory stack, as it would be displayed by the dirs built-in invoked with the characters following tilde in the tilde-prefix as an argument. If the tilde-prefix, without the tilde, consists of a number without a leading "+" or "-", "+" is assumed.

    If the login name is invalid, or the tilde expansion fails, the word is left unchanged.

    Each variable assignment is checked for unquoted tilde-prefixes immediately following a ":" or "=". In these cases, tilde expansion is also performed. Consequently, one may use file names with tildes in assignments to PATH, MAILPATH, and CDPATH, and the shell assigns the expanded value.

    Example:

    pbmac@pbmac-server $ export PATH="$PATH:~/testdir"
    

    ~/testdir will be expanded to $HOME /testdir , so if $HOME is /var/home/franky , the directory /var/home/franky/testdir will be added to the content of the PATH variable.

    Shell Parameter and Variable 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.

    When braces are used, the matching ending brace is the first "}" not escaped by a backslash or within a quoted string, and not within an embedded arithmetic expansion, command substitution, or parameter expansion.

    The basic form of parameter expansion is "${PARAMETER}". The value of "PARAMETER" is substituted. The braces are required when "PARAMETER" is a positional parameter with more than one digit, or when "PARAMETER" is followed by a character that is not to be interpreted as part of its name.

    If the first character of "PARAMETER" is an exclamation point, Bash uses the value of the variable formed from the rest of "PARAMETER" as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of "PARAMETER" itself. This is known as indirect expansion.

    You are certainly familiar with straight parameter expansion, since it happens all the time, even in the simplest of cases, such as the one above or the following:

    pbmac@pbmac-server $ echo  $SHELL 
    /bin/bash

    The following is an example of indirect expansion:

    pbmac@pbmac-server $ echo  ${!N*} 
    NNTPPORT NNTPSERVER NPX_PLUGIN_PATH

    Note that this is not the same as echo $N* .

    The following construct allows for creation of the named variable if it does not yet exist:

    ${ VAR := value }

    Example:

    pbmac@pbmac-server $ echo  $PBMAC
     
    pbmac@pbmac-server $ echo  ${PBMAC:=pbmac}
    pbmac
    

    However special parameters, among others the positional parameters, may not be assigned this way.

    Command Substitution

    Command substitution allows the output of a command to replace the command itself. Command substitution occurs when a command is enclosed like this:

    $(command)

    or like this using backticks:

    `command`

    Bash performs the expansion by executing COMMAND and replacing the command substitution with the standard output of the command, with any trailing newlines deleted. Embedded newlines are not deleted, but they may be removed during word splitting.

    pbmac@pbmac-server $ echo `date`
    Thu Nov 19 17:44:55 PST 2020
    

    When the old-style backquoted form of substitution is used, backslash retains its literal meaning except when followed by "$", "`", or "\". The first backticks not preceded by a backslash terminates the command substitution. When using the "$(COMMAND)" form, all characters between the parentheses make up the command; none are treated specially.

    Command substitutions may be nested. To nest when using the backquoted form, escape the inner backticks with backslashes.

    If the substitution appears within double quotes, word splitting and file name expansion are not performed on the results.

    Arithmetic Expansion

    Arithmetic expansion allows the evaluation of an arithmetic expression and the substitution of the result. The format for arithmetic expansion is:

    $(( EXPRESSION ))

    The expression is treated as if it were within double quotes, but a double quote inside the parentheses is not treated specially. All tokens in the expression undergo parameter expansion, command substitution, and quote removal. Arithmetic substitutions may be nested.

    Evaluation of arithmetic expressions is done in fixed-width integers with no check for overflow, although division by zero is trapped and recognized as an error. The operators are roughly the same as in the C programming language. In order of decreasing precedence, the list looks like this:

    Arithmetic operators

    Operator Meaning
    VAR++ and VAR-- Variable post-increment and post-decrement
    ++VAR and --VAR Variable pre-increment and pre-decrement
    - and + Unary minus and plus
    ! and ~ Logical and bitwise negation
    ** Exponentiation
    *, / and % Multiplication, division, remainder
    + and - Addition, subtraction
    << and >> Left and right bitwise shifts
    <=, >=, < and > Comparison operators
    == and != Equality and inequality
    & Bitwise AND
    ^ Bitwise exclusive OR
    | Bitwise OR
    && Logical AND
    || Logical OR
    expr ? expr : expr Conditional evaluation
    =, *=, /=, %=, +=, -=, <<=, >>=, &=, ^= and |= Assignments
    , Separator between expressions

    Shell variables are allowed as operands; parameter expansion is performed before the expression is evaluated. Within an expression, shell variables may also be referenced by name without using the parameter expansion syntax. The value of a variable is evaluated as an arithmetic expression when it is referenced. A shell variable need not have its integer attribute turned on to be used in an expression.

    Constants with a leading 0 (zero) are interpreted as octal numbers. A leading "0x" or "0X" denotes hexadecimal. Otherwise, numbers take the form "[BASE'#']N", where "BASE" is a decimal number between 2 and 64 representing the arithmetic base, and N is a number in that base. If "BASE'#'" is omitted, then base 10 is used. The digits greater than nine are represented by the lowercase letters, the uppercase letters, "@", and "_", in that order. If "BASE" is less than or equal to 36, lowercase and uppercase letters may be used interchangeably to represent numbers between 10 and 35.

    Operators are evaluated in order of precedence. Sub-expressions in parentheses are evaluated first and may override the precedence rules above.

    Wherever possible, Bash users should try to use the syntax with square brackets:

    $[ EXPRESSION ]

    However, this will only calculate the result of EXPRESSION, and do no tests:

    pbmac@pbmac-server $ echo  $[365*24]
    8760
    

    Globbing

    Bash does carry out filename expansion -- a process known as globbing -- but this does not use the standard RE set. Instead, globbing recognizes and expands wild cards. Globbing interprets the standard wild card characters -- * and ?, character lists in square brackets, and certain other special characters (such as ^ for negating the sense of a match). There are important limitations on wild card characters in globbing, however. Strings containing * will not match filenames that start with a dot, as, for example, .bashrc. Likewise, the ? has a different meaning in globbing than as part of a regular expression.

    pbmac@pbmac-server $ ls -l
    total 2
    -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 a.1
    -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 b.1
    -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 c.1
    -rw-rw-r--    1 bozo  bozo       466 Aug  6 17:48 t2.sh
    -rw-rw-r--    1 bozo  bozo       758 Jul 30 09:02 test1.txt
    
    pbmac@pbmac-server $ ls -l t?.sh
    -rw-rw-r--    1 bozo  bozo       466 Aug  6 17:48 t2.sh
    
    pbmac@pbmac-server $ ls -l [ab]*
    -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 a.1
    -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 b.1
    
    pbmac@pbmac-server $ ls -l [a-c]*
    -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 a.1
    -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 b.1
    -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 c.1
    
    pbmac@pbmac-server $ ls -l [^ab]*
    -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 c.1
    -rw-rw-r--    1 bozo  bozo       466 Aug  6 17:48 t2.sh
    -rw-rw-r--    1 bozo  bozo       758 Jul 30 09:02 test1.txt
    
    pbmac@pbmac-server $ ls -l {b*,c*,*est*}
    -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 b.1
    -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 c.1
    -rw-rw-r--    1 bozo  bozo       758 Jul 30 09:02 test1.txt
    
    pbmac@pbmac-server $ echo *
    a.1 b.1 c.1 t2.sh test1.txt
    
    pbmac@pbmac-server $ echo t*
    t2.sh test1.txt
    
    pbmac@pbmac-server $ echo t?.sh
    

    Bash performs filename expansion on unquoted command-line arguments. The echo command demonstrates this.

    pbmac@pbmac-server $ echo *
    a.1 b.1 c.1 t2.sh test1.txt
    
    pbmac@pbmac-server $ echo t*
    t2.sh test1.txt
    
    pbmac@pbmac-server $ echo t?.sh
    t2.sh
    

    Adapted from:
    "3.4. Shell expansion" by Multiple Contributors, The Linux Documentation Project is licensed under CC BY-SA 3.0
    "18.2. Globbing" by Multiple Contributors, The Linux Documentation Project is licensed under CC BY-SA 3.0


    13-C.7: Shell Expansion / Variable Substitution / Command Substitution is shared under a CC BY-SA 4.0 license and was authored, remixed, and/or curated by LibreTexts.

    • Was this article helpful?