Differences between revisions 5 and 6
Revision 5 as of 2007-11-30 03:18:01
Size: 4872
Editor: GreyCat
Comment: link to #5 (arrays)
Revision 6 as of 2008-04-18 15:25:25
Size: 5808
Editor: GreyCat
Comment: expand the portability section
Deletions are marked like this. Additions are marked like this.
Line 43: Line 43:
  # Note: string:-1 means something entirely different.   # Note: string:-1 means something entirely different; see below.
Line 93: Line 93:
==== Portability ====
Since arrays are not portable, nor is their expansion.
(Anyone know if ksh does this?)
=== 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 options available.

POSIX (as well as Korn and Bash) shells 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 also 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?

Parameter Expansion is a separate section of the [http://tiswww.case.edu/php/chet/bash/bash.html#lbBA bash manpage] (also man bash -P 'less -p "^   Parameter Expansion"' 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. (DO NOT think about parameter expansion like a regex. It is different and distinct.)

Here's an example of how to use 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            

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.

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.

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#faq5:BASH arrays] are remarkably flexible, since they are so well integrated with the other shell expansions. Any expansion you can carry out on a scalar can equally be applied to the whole list in an array. Remember that quoting an array expansion using @, eg "$@" or "${cmd[@]}" results in the members being treated as individual words, irrespective of their content. So for example, arr=("${list[@]}" foo) correctly handles all parameters in the list; we use this to modify an array in place.

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.

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 options available.

POSIX (as well as Korn and Bash) shells 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 also 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 2021-11-09 19:48:16 by geirha)