Differences between revisions 1 and 9 (spanning 8 versions)
Revision 1 as of 2007-05-03 00:09:05
Size: 2873
Editor: redondos
Comment:
Revision 9 as of 2008-09-20 13:11:35
Size: 6651
Editor: GreyCat
Comment: *sigh*
Deletions are marked like this. Additions are marked like this.
Line 4: Line 4:
Parameter expansion is a separate section of the bash manpage ({{{man bash -P 'less -p "^ Parameter Expansion"'}}} or [http://tiswww.tis.case.edu/~chet/bash/bashref.html#SEC30 see the reference]). It can be hard to understand parameter expansion without actually using it. (DO NOT think about parameter expansion like a regex. It is different and distinct.) Your shell has ''variables'' (either inherited from the environment, or defined by you), as well as several other ''special parameters'' like `$` (the shell's process ID) and so on. All of these are considered ''parameters'', and they can be expanded by putting a `$` in front. However, that's just the simplest case.
Line 6: Line 6:
Here's an example of how to use parameter expansion with something akin to a hostname (dot-separated components): Parameter Expansion is far more general and powerful than that. (The full set of capabilities can be found in the [http://tiswww.case.edu/php/chet/bash/bash.html#lbBA bash manpage], or see [http://tiswww.tis.case.edu/~chet/bash/bashref.html#SEC30 the reference] or [http://bash-hackers.org/wiki/doku.php?id=syntax:pe the bash hackers article] about it). It can be hard to understand parameter expansion without actually using it, so we're going to begin with some examples.
Line 8: Line 8:
The first set of capabilities involves removing a substring, from either the beginning or the end of a parameter. Here's an example using parameter expansion with something akin to a hostname (dot-separated components):
Line 18: Line 19:
And, here's an example of the parameter expansions for a typical filename.
And here's an example of the parameter expansions for a typical filename:
Line 30: Line 30:
You cannot nest parameter expansions. If you need to perform two separate expansions, use a temporary variable to hold the result of the first expansion. US keyboard users may find it helpful to observe that, on the keyboard, the "#" is to the left of the "%" symbol. Mnemonically, "#" operates on the left side of a parameter, and "%" operates on the right. The [:glob:] after the "%" or "%%" or "#" or "##" specifies what pattern to ''remove'' from the parameter expansion.
Line 32: Line 32:
You may find it helpful to associate that, on your keyboard, the "#" is to the left of the "$" symbol and the "%" symbol is to its right; this corresponds with their acting upon the left (beginning) and right (end) parts of the parameter. You cannot nest parameter expansions. If you need to perform two expansions steps, use a variable to hold the result of the first expansion:
{{{
# foo holds: key="some value"
bar=${foo#*=\"} bar=${bar%\"*}
# now bar holds: some value
}}}
Line 35: Line 40:
Line 43: Line 47:
  # Note: string:-1 means something entirely different.   # Note: string:-1 means something entirely different; see below.
Line 52: Line 56:

=== Parameter Expansion on Arrays ===
[:BashFAQ/005:BASH arrays] are remarkably flexible, since they are so well integrated with the other shell expansions. Virtually any expansion you can carry out on a scalar can equally be applied to a whole array. Remember that quoting an array expansion using @ (e.g. {{{"$@"}}} or {{{"${cmd[@]}"}}}) results in the members being treated as individual words, regardless of their content. So for example, {{{arr=("${list[@]}" foo)}}} correctly handles all elements in the `list` array.

First the expansions:
{{{
$ a=(alpha beta gamma) # our base array
$ echo "${a[@]#a}" # chop 'a' from the beginning of every member
lpha beta gamma
$ echo "${a[@]%a}" # from the end
alph bet gamm
$ echo "${a[@]//a/f}" # substitution
flphf betf gfmmf
}}}

The following expansions (substitute at beginning or end) are very useful for the next part:
{{{
$ echo "${a[@]/#a/f}" # substitute a for f at start
flpha beta gamma
$ echo "${a[@]/%a/f}" # at end
alphf betf gammf
}}}

We use these to prefix or suffix every member of the list:
{{{
$ echo "${a[@]/#/a}" # append a to beginning
aalpha abeta agamma # (thanks to floyd-n-milan for this)
$ echo "${a[@]/%/a}" # append a to end
alphaa betaa gammaa
}}}
This works by substituting an empty string at beginning or end with the value we wish to append.

So finally, a quick example of how you might use this in a script, say to add a user-defined prefix to every target:
{{{
$ PFX=inc_
$ a=("${a[@]/#/$PFX}")
$ echo "${a[@]}"
inc_alpha inc_beta inc_gamma
}}}
This is very useful, as you might imagine, since it saves looping over every member of the array.

The special parameter `@` can also be used as an array for purposes of parameter expansions:
{{{
${@:(-2):1} # the second-to-last parameter
${@: -2:1} # alternative syntax
}}}

You can't use `${@:-2:1}`, however, because that collides with the syntax in the next section.

=== Portability ===
The original Bourne shell (7th edition Unix) only supports a very limited set of parameter expansion options:
{{{
${var-word} # if var is defined, use var; otherwise, "word"
${var+word} # if var is defined, use "word"; otherwise, nothing
${var=word} # if var is defined, use var; otherwise, use "word" AND...
   # also assign "word" to var
${var?error} # if var is defined, use var; otherwise print "error" and exit
}}}
These are the only completely portable expansions available.

POSIX shells (as well as KornShell and [:BASH:]) offer those, plus a slight variant:
{{{
${var:-word} # if var is defined AND NOT EMPTY, use var; otherwise, "word"
etc.
}}}

POSIX, Korn (all versions) and Bash all support the `${var#word}`, `${var%word}`, `${var##word}` and `${var%%word}` expansions.

ksh88 does not support `${var/replace/with}` or `${var//replace/all}`, but ksh93 and Bash do.

ksh88 does not support fancy expansion with arrays (e.g., `${a[@]%.gif}`) but ksh93 and Bash do.

Anchor(faq73)

How can I use parameter expansion? How can I get substrings? How can I get a file without its extension, or get just a file's extension?

Your shell has variables (either inherited from the environment, or defined by you), as well as several other special parameters like $ (the shell's process ID) and so on. All of these are considered parameters, and they can be expanded by putting a $ in front. However, that's just the simplest case.

Parameter Expansion is far more general and powerful than that. (The full set of capabilities can be found in the [http://tiswww.case.edu/php/chet/bash/bash.html#lbBA bash manpage], or see [http://tiswww.tis.case.edu/~chet/bash/bashref.html#SEC30 the reference] or [http://bash-hackers.org/wiki/doku.php?id=syntax:pe the bash hackers article] about it). It can be hard to understand parameter expansion without actually using it, so we're going to begin with some examples.

The first set of capabilities involves removing a substring, from either the beginning or the end of a parameter. Here's an example using parameter expansion with something akin to a hostname (dot-separated components):

parameter     result
-----------   ------------------------------
${NAME}       polish.ostrich.racing.champion
${NAME#*.}           ostrich.racing.champion
${NAME##*.}                         champion
${NAME%%.*}   polish                        
${NAME%.*}    polish.ostrich.racing         

And here's an example of the parameter expansions for a typical filename:

parameter     result
-----------   --------------------------------------------------------
${FILE}       /usr/share/java-1.4.2-sun/demo/applets/Clock/Clock.class
${FILE#*/}     usr/share/java-1.4.2-sun/demo/applets/Clock/Clock.class
${FILE##*/}                                                Clock.class
${FILE%%/*}                                                           
${FILE%/*}    /usr/share/java-1.4.2-sun/demo/applets/Clock            

US keyboard users may find it helpful to observe that, on the keyboard, the "#" is to the left of the "%" symbol. Mnemonically, "#" operates on the left side of a parameter, and "%" operates on the right. The [:glob:] after the "%" or "%%" or "#" or "##" specifies what pattern to remove from the parameter expansion.

You cannot nest parameter expansions. If you need to perform two expansions steps, use a variable to hold the result of the first expansion:

# foo holds: key="some value"
bar=${foo#*=\"} bar=${bar%\"*}
# now bar holds: some value

Here are a few more examples (but please see the real documentation for a list of all the features!). I include these mostly so people won't break the wiki again, trying to add new questions that answer this stuff.

${string:2:1}   # The third character of string (0, 1, 2 = third)
${string:1}     # The string starting from the second character
                # Note: this is equivalent to ${string#?}
${string%?}     # The string with its last character removed.
${string: -1}   # The last character of string
${string:(-1)}  # The last character of string, alternate syntax
                # Note: string:-1 means something entirely different; see below.

${file%.mp3}    # The filename without the .mp3 extension
                # Very useful in loops of the form: for file in *.mp3; do ...
${file%.*}      # The filename without its extension (assuming there was
                # only one extension in the first place...).
${file%%.*}     # The filename without all of its extensions
${file##*.}     # The extension only.

Parameter Expansion on Arrays

[:BashFAQ/005:BASH arrays] are remarkably flexible, since they are so well integrated with the other shell expansions. Virtually any expansion you can carry out on a scalar can equally be applied to a whole array. Remember that quoting an array expansion using @ (e.g. "$@" or "${cmd[@]}") results in the members being treated as individual words, regardless of their content. So for example, arr=("${list[@]}" foo) correctly handles all elements in the list array.

First the expansions:

$ a=(alpha beta gamma)  # our base array
$ echo "${a[@]#a}"      # chop 'a' from the beginning of every member
lpha beta gamma
$ echo "${a[@]%a}"      # from the end
alph bet gamm
$ echo "${a[@]//a/f}"   # substitution
flphf betf gfmmf

The following expansions (substitute at beginning or end) are very useful for the next part:

$ echo "${a[@]/#a/f}"   # substitute a for f at start
flpha beta gamma
$ echo "${a[@]/%a/f}"   # at end
alphf betf gammf

We use these to prefix or suffix every member of the list:

$ echo "${a[@]/#/a}"    # append a to beginning
aalpha abeta agamma     #    (thanks to floyd-n-milan for this)
$ echo "${a[@]/%/a}"    # append a to end
alphaa betaa gammaa

This works by substituting an empty string at beginning or end with the value we wish to append.

So finally, a quick example of how you might use this in a script, say to add a user-defined prefix to every target:

$ PFX=inc_
$ a=("${a[@]/#/$PFX}")
$ echo "${a[@]}"
inc_alpha inc_beta inc_gamma

This is very useful, as you might imagine, since it saves looping over every member of the array.

The special parameter @ can also be used as an array for purposes of parameter expansions:

${@:(-2):1}             # the second-to-last parameter
${@: -2:1}              # alternative syntax

You can't use ${@:-2:1}, however, because that collides with the syntax in the next section.

Portability

The original Bourne shell (7th edition Unix) only supports a very limited set of parameter expansion options:

${var-word}             # if var is defined, use var; otherwise, "word"
${var+word}             # if var is defined, use "word"; otherwise, nothing
${var=word}             # if var is defined, use var; otherwise, use "word" AND...
                        #   also assign "word" to var
${var?error}            # if var is defined, use var; otherwise print "error" and exit

These are the only completely portable expansions available.

POSIX shells (as well as KornShell and [:BASH:]) offer those, plus a slight variant:

${var:-word}             # if var is defined AND NOT EMPTY, use var; otherwise, "word"
etc.

POSIX, Korn (all versions) and Bash all support the ${var#word}, ${var%word}, ${var##word} and ${var%%word} expansions.

ksh88 does not support ${var/replace/with} or ${var//replace/all}, but ksh93 and Bash do.

ksh88 does not support fancy expansion with arrays (e.g., ${a[@]%.gif}) but ksh93 and Bash do.

BashFAQ/073 (last edited 2023-06-24 09:33:51 by emanuele6)