Advanced Bash-Scripting Guide: An in-depth exploration of the art of shell scripting
Prev | Chapter 37. Bash, versions 2, 3, and 4 | Next

# 37.2. Bash, version 3

On July 27, 2004, Chet Ramey released version 3 of Bash. This update fixed quite a number of bugs and added new features.

Some of the more important added features:

- A new, more generalized {a..z} brace expansion operator.

         1 #!/bin/bash
         2
         3 for i in {1..10}
         4 #  Simpler and more straightforward than
         5 #+ for i in $(seq 10)
         6 do
         7   echo -n "$i "
         8 done
         9
        10 echo
        11
        12 # 1 2 3 4 5 6 7 8 9 10
        13
        14
        15
        16 # Or just . . .
        17
        18 echo {a..z}    #  a b c d e f g h i j k l m n o p q r s t u v w x y z
        19 echo {e..m}    #  e f g h i j k l m
        20 echo {z..a}    #  z y x w v u t s r q p o n m l k j i h g f e d c b a
        21                #  Works backwards, too.
        22 echo {25..30}  #  25 26 27 28 29 30
        23 echo {3..-2}   #  3 2 1 0 -1 -2
        24 echo {X..d}    #  X Y Z [  ] ^ _ ` a b c d
        25                #  Shows (some of) the ASCII characters between Z and a,
        26                #+ but don't rely on this type of behavior because . . .
        27 echo {]..a}    #  {]..a}
        28                #  Why?
        29
        30
        31 # You can tack on prefixes and suffixes.
        32 echo "Number #"{1..4}, "..."
        33      # Number #1, Number #2, Number #3, Number #4, ...
        34
        35
        36 # You can concatenate brace-expansion sets.
        37 echo {1..3}{x..z}" +" "..."
        38      # 1x + 1y + 1z + 2x + 2y + 2z + 3x + 3y + 3z + ...
        39      # Generates an algebraic expression.
        40      # This could be used to find permutations.
        41
        42 # You can nest brace-expansion sets.
        43 echo {{a..c},{1..3}}
        44      # a b c 1 2 3
        45      # The "comma operator" splices together strings.
        46
        47 # ########## ######### ############ ########### ######### ###############
        48 # Unfortunately, brace expansion does not lend itself to parameterization.
        49 var1=1
        50 var2=5
        51 echo {$var1..$var2}   # {1..5}
        52
        53
        54 # Yet, as Emiliano G. points out, using "eval" overcomes this limitation.
        55
        56 start=0
        57 end=10
        58 for index in $(eval echo {$start..$end})
        59 do
        60   echo -n "$index "   # 0 1 2 3 4 5 6 7 8 9 10
        61 done
        62
        63 echo

- The ${!array[@]} operator, which expands to all the indices of a given array.

         1 #!/bin/bash
         2
         3 Array=(element-zero element-one element-two element-three)
         4
         5 echo ${Array[0]}   # element-zero
         6                    # First element of array.
         7
         8 echo ${!Array[@]}  # 0 1 2 3
         9                    # All the indices of Array.
        10
        11 for i in ${!Array[@]}
        12 do
        13   echo ${Array[i]} # element-zero
        14                    # element-one
        15                    # element-two
        16                    # element-three
        17                    #
        18                    # All the elements in Array.
        19 done

- The =~ Regular Expression matching operator within a double brackets test expression. (Perl has a similar operator.)

         1 #!/bin/bash
         2
         3 variable="This is a fine mess."
         4
         5 echo "$variable"
         6
         7 # Regex matching with =~ operator within [[ double brackets ]].
         8 if [[ "$variable" =~ T.........fin*es* ]]
         9 # NOTE: As of version 3.2 of Bash, expression to match no longer quoted.
        10 then
        11   echo "match found"
        12       # match found
        13 fi

  Or, more usefully:

         1 #!/bin/bash
         2
         3 input=$1
         4
         5
         6 if [[ "$input" =~ "[0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]" ]]
         7 #                 ^ NOTE: Quoting not necessary, as of version 3.2 of Bash.
         8 # NNN-NN-NNNN (where each N is a digit).
         9 then
        10   echo "Social Security number."
        11   # Process SSN.
        12 else
        13   echo "Not a Social Security number!"
        14   # Or, ask for corrected input.
        15 fi

  For additional examples of using the =~ operator, see Example A-29, Example 19-14, Example A-35, and Example A-24.

- The new `set -o pipefail` option is useful for debugging pipes. If this option is set, then the exit status of a pipe is the exit status of the last command in the pipe to fail (return a non-zero value), rather than the actual final command in the pipe.

  See Example 16-43.

| The update to version 3 of Bash breaks a few scripts that worked under earlier versions. Test critical legacy scripts to make sure they still work! As it happens, a couple of the scripts in the Advanced Bash Scripting Guide had to be fixed up (see Example 9-4, for instance).

## 37.2.1. Bash, version 3.1

The version 3.1 update of Bash introduces a number of bugfixes and a few minor changes.

- The += operator is now permitted in in places where previously only the = assignment operator was recognized.

         1 a=1
         2 echo $a        # 1
         3
         4 a+=5           # Won't work under versions of Bash earlier than 3.1.
         5 echo $a        # 15
         6
         7 a+=Hello
         8 echo $a        # 15Hello

  Here, += functions as a string concatenation operator. Note that its behavior in this particular context is different than within a let construct.

         1 a=1
         2 echo $a        # 1
         3
         4 let a+=5       # Integer arithmetic, rather than string concatenation.
         5 echo $a        # 6
         6
         7 let a+=Hello   # Doesn't "add" anything to a.
         8 echo $a        # 6

  Jeffrey Haemer points out that this concatenation operator can be quite useful. In this instance, we append a directory to the `$PATH`.

       bash$ echo $PATH
       /usr/bin:/bin:/usr/local/bin:/usr/X11R6/bin/:/usr/games

       bash$ PATH+=:/opt/bin

       bash$ echo $PATH
       /usr/bin:/bin:/usr/local/bin:/usr/X11R6/bin/:/usr/games:/opt/bin

## 37.2.2. Bash, version 3.2

This is pretty much a bugfix update.

- In global parameter substitutions, the pattern no longer anchors at the start of the string.

- The `--wordexp` option disables process substitution.

- The =~ Regular Expression match operator no longer requires quoting of the pattern within [[ ... ]].

  | In fact, quoting in this context is not advisable as it may cause regex evaluation to fail. Chet Ramey states in the Bash FAQ that quoting explicitly disables regex evaluation. See also the Ubuntu Bug List and Wikinerds on Bash syntax. Setting shopt -s compat31 in a script causes reversion to the original behavior.

Prev | Home | Next
Bash, versions 2, 3, and 4 | Up | Bash, version 4