| Advanced Bash-Scripting Guide: A complete guide to shell scripting, using Bash | ||
|---|---|---|
| Prev | Chapter 3. Tutorial / Reference | Next |
3.3. Introduction to Variables and Parameters
Variables are at the heart of every programming and scripting language. They appear in arithmetic operations and manipulation of quantities, string parsing, and are indispensable for working in the abstract with symbols - tokens that represent something else. A variable is nothing more than a location or set of locations in computer memory that holds an item of data.
- $
variable substitution. Let us carefully distinguish between the name of a variable and its value. If variable1 is the name of a variable, then $variable1 is a reference to its value, the data item it contains. The only time a variable appears "naked", without the $ prefix, is when declared or assigned, when unset, or when exported. Assignment may be with an = (as in var1=27), in a read statement, and at the head of a loop (for var2 in 1 2 3).
Enclosing a referenced value in double quotes (" ") does not interfere with variable substitution. This is called partial quoting, sometimes referred to as "weak quoting". Using single quotes (' ') causes the variable name to be used literally, and no substitution will take place. This is full quoting, sometimes referred to as "strong quoting". See Section 3.4 for a detailed discussion.
Note that $variable is actually a simplified alternate form of ${variable}. In contexts where the $variable syntax causes an error, the longer form may work (see Section 3.3.1, below).
Example 3-6. Variable assignment and substitution
1 #!/bin/bash 2 3 # Variables: assignment and substitution 4 5 a=37.5 6 hello=$a 7 8 #------------------------------------------------------------------------- 9 # No space permitted on either side of = sign when initializing variables. 10 11 # If "VARIABLE =value", 12 # script tries to run "VARIABLE" command with one argument, "=value". 13 14 # If "VARIABLE= value", 15 # script tries to run "value" command with 16 # the environmental variable "VARIABLE" set to "". 17 #------------------------------------------------------------------------- 18 19 20 echo hello 21 # Not a reference. 22 23 echo $hello 24 echo ${hello} #Identical to above. 25 26 echo "$hello" 27 echo "${hello}" 28 29 # hello="A B C D" 30 # Now, echo $hello and echo "$hello" give different results. 31 # Quoting variable perserves whitespace. 32 33 echo '$hello' 34 # Variable referencing disabled by single quotes, 35 # because $ interpreted literally. 36 37 # Notice the effect of different types of quoting. 38 39 40 # Now *unsetting* $hello. 41 hello= 42 echo "\$hello (unset) = $hello" 43 # Unsetting a variable means setting it to a null value. 44 45 # -------------------------------------------------------------- 46 47 # It is permissible to set multiple variables on the same line, 48 # separated by white space. 49 # Careful, this may reduce legibility, and may not be portable. 50 51 var1=variable1 var2=variable2 var3=variable3 52 echo 53 echo "var1=$var1 var2=$var2 var3=$var3" 54 55 # This may cause problems with older versions of "sh". 56 57 # -------------------------------------------------------------- 58 59 echo; echo 60 61 numbers="one two three" 62 other_numbers="1 2 3" 63 # If whitespace within variables, then quotes necessary. 64 echo "numbers = $numbers" 65 echo "other_numbers = $other_numbers" 66 echo 67 68 echo "uninitialized variable = $uninitialized_variable" 69 # Uninitialized variable has null value (no value at all). 70 71 echo 72 73 exit 0
An uninitialized variable has a "null" value - no assigned value at all (not zero!). Using a variable before assigning a value to it will inevitably cause problems.
3.3.1. Parameter Substitution
- ${parameter}
Same as $parameter, i.e., value of the variable parameter.
May be used for concatenating variables with strings.
1 your_id=${USER}-on-${HOSTNAME} 2 echo "$your_id" 3 # 4 echo "Old \$PATH = $PATH" 5 PATH=${PATH}:/opt/bin #Add /opt/bin to $PATH for duration of script. 6 echo "New \$PATH = $PATH"- ${parameter-default}
If parameter not set, use default.
1 echo ${username-`whoami`} 2 # Echoes the result of `whoami`, but variable "username" is still unset.
This is almost equivalent to ${parameter:-default}. The extra : makes a difference only when parameter has been declared, but is null.
1 #!/bin/bash 2 3 username0= 4 echo "username0 = ${username0-`whoami`}" 5 # username0 has been declared, but is set to null. 6 # Will not echo. 7 8 echo "username1 = ${username1-`whoami`}" 9 # username1 has not been declared. 10 # Will echo. 11 12 username2= 13 echo "username2 = ${username2:-`whoami`}" 14 # username2 has been declared, but is set to null. 15 # Will echo because of :- rather than just - in condition test. 16 17 exit 0- ${parameter=default}, ${parameter:=default}
If parameter not set, set it to default.
Both forms nearly equivalent. The : makes a difference only when $parameter has been declared and is null, [1] as above.
1 echo ${username=`whoami`} 2 # Variable "username" is now set to `whoami`.- ${parameter+otherwise}, ${parameter:+otherwise}
If parameter set, use 'otherwise", else use null string.
Both forms nearly equivalent. The : makes a difference only when parameter has been declared and is null, as above.
- ${parameter?err_msg}, ${parameter:?err_msg}
If parameter set, use it, else print err_msg.
Both forms nearly equivalent. The : makes a difference only when parameter has been declared and is null, as above.
Example 3-7. Using param substitution and :
1 #!/bin/bash
2
3 # Let's check some of the system's environmental variables.
4 # If, for example, $USER, the name of the person at the console, is not set,
5 # the machine will not recognize you.
6
7 : ${HOSTNAME?} ${USER?} ${HOME?} ${MAIL?}
8 echo
9 echo "Name of the machine is $HOSTNAME."
10 echo "You are $USER."
11 echo "Your home directory is $HOME."
12 echo "Your mail INBOX is located in $MAIL."
13 echo
14 echo "If you are reading this message,"
15 echo "critical environmental variables have been set."
16 echo
17 echo
18
19 # ------------------------------------------------------
20
21 # The ${variablename?} construction can also check
22 # for variables set within the script.
23
24 ThisVariable=Value-of-ThisVariable
25 # Note, by the way, that string variables may be set
26 # to characters disallowed in their names.
27 : ${ThisVariable?}
28 echo "Value of ThisVariable is $ThisVariable".
29 echo
30 echo
31
32
33 : ${ZZXy23AB?"ZZXy23AB has not been set."}
34 # If ZZXy23AB has not been set, then the script terminates with an error message.
35
36 # You can specify the error message.
37 # : ${ZZXy23AB?"ZZXy23AB has not been set."}
38
39
40 # Same result with: dummy_variable=${ZZXy23AB?}
41 # dummy_variable=${ZZXy23AB?"ZXy23AB has not been set."}
42 #
43 # echo ${ZZXy23AB?} >/dev/null
44
45
46
47 echo "You will not see this message, because script terminated above."
48
49 HERE=0
50 exit $HERE # Will *not* exit here. |
Parameter substitution and/or expansion. The following expressions are the complement to the match in expr string operations (see Example 3-88). These particular ones are used mostly in parsing file path names.
- ${#var}
String length (number of characters in $var). For an array, ${#array} is the length of the first element in the array.

Exceptions:
${#*} and ${#@} give the number of positional parameters.
For an array, ${#array[*]} and ${#array[@]} give the number of elements in the array.
Example 3-8. Length of a variable
1 #!/bin/bash 2 # length.sh 3 4 E_NOT_ENOUGH_ARGS=65 5 6 if [ $# -eq 0 ] # Must have command-line args to demo script. 7 then 8 echo "Invoke this script with one or more command-line arguments." 9 exit $E_NOT_ENOUGH_ARGS 10 fi 11 12 var01=abcdEFGH28ij 13 14 echo "var01 = ${var01}" 15 echo "Length of var01 = ${#var01}" 16 17 echo "Number of command-line arguments passed to script = ${#@}" 18 echo "Number of command-line arguments passed to script = ${#*}" 19 20 exit 0- ${var#pattern}, ${var##pattern}
Remove from $var the shortest/longest part of $pattern that matches the front end of $var.
- ${var%pattern}, ${var%%pattern}
Remove from $var the shortest/longest part of $pattern that matches the back end of $var.
Version 2 of bash adds additional options.
Example 3-9. Pattern matching in parameter substitution
1 #!/bin/bash
2 # Pattern matching using the # ## % %% parameter substitution operators.
3
4 var1=abcd12345abc6789
5 pattern1=a*c # * (wild card) matches everything between a - c.
6
7 echo
8 echo "var1 = $var1" # abcd12345abc6789
9 echo "var1 = ${var1}" # abcd12345abc6789 (alternate form)
10 echo "Number of characters in ${var1} = ${#var1}"
11 echo "pattern1 = $pattern1" # a*c (everything between 'a' and 'c')
12 echo
13
14
15 echo '${var1#$pattern1} =' "${var1#$pattern1}" # d12345abc6789
16 # Shortest possible match, strips out first 3 characters abcd12345abc6789
17 # ^^^^^ |-|
18 echo '${var1##$pattern1} =' "${var1##$pattern1}" # 6789
19 # Longest possible match, strips out first 12 characters abcd12345abc6789
20 # ^^^^^ |----------|
21
22 echo; echo
23
24 pattern2=b*9 # everything between 'b' and '9'
25 echo "var1 = $var1" # Still abcd12345abc6789
26 echo "pattern2 = $pattern2"
27 echo
28
29 echo '${var1%pattern2} =' "${var1%$pattern2}" # abcd12345a
30 # Shortest possible match, strips out last 6 characters abcd12345abc6789
31 # ^^^^ |----|
32 echo '${var1%%pattern2} =' "${var1%%$pattern2}" # a
33 # Longest possible match, strips out last 12 characters abcd12345abc6789
34 # ^^^^ |-------------|
35
36 # Remember, # and ## work from the left end of string,
37 # % and %% work from the right end of string.
38
39 echo
40
41 exit 0 |
Example 3-10. Renaming file extensions:
1 #!/bin/bash
2
3 # rfe
4 # ---
5
6 # Renaming file extensions.
7 #
8 # rfe old_extension new_extension
9 #
10 # Example:
11 # To rename all *.gif files in working directory to *.jpg,
12 # rfe gif jpg
13
14 ARGS=2
15 E_BADARGS=65
16
17 if [ $# -ne $ARGS ]
18 then
19 echo "Usage: `basename $0` old_file_suffix new_file_suffix"
20 exit $E_BADARGS
21 fi
22
23 for filename in *.$1
24 # Traverse list of files ending with 1st argument.
25 do
26 mv $filename ${filename%$1}$2
27 # Strip off part of filename matching 1st argument,
28 # then append 2nd argument.
29 done
30
31 exit 0 |
- ${var:pos}
Variable var expanded, starting from offset pos.
This expansion adopted from ksh93.
- ${var:pos:len}
Expansion to a max of len characters of variable var, from offset pos. See Example A-8 for an example of the creative use of this operator.
This expansion adopted from ksh93.
- ${var/patt/replacement}
First match of patt, within var replaced with replacement.
If replacement is omitted, then the first match of patt is replaced by nothing, that is, deleted.
- ${var//patt/replacement}
All matches of patt, within var replaced with replacement.
Similar to above, if replacement is omitted, then all occurrences patt are replaced by nothing, that is, deleted.
Example 3-11. Using pattern matching to parse arbitrary strings
1 #!/bin/bash
2
3 var1=abcd-1234-defg
4 echo "var1 = $var1"
5
6 t=${var1#*-*}
7 echo "var1 (with everything, up to and including first - stripped out) = $t"
8 # t=${var1#*-} works just the same,
9 # since # matches the shortest string,
10 # and * matches everything preceding, including an empty string.
11 # (Thanks, S. C. for pointing this out.)
12
13 t=${var1##*-*}
14 echo "If var1 contains a \"-\", returns empty string... var1 = $t"
15
16
17 t=${var1%*-*}
18 echo "var1 (with everything from the last - on stripped out) = $t"
19
20 echo
21
22 path_name=/home/bozo/ideas/thoughts.for.today
23 echo "path_name = $path_name"
24 t=${path_name##/*/}
25 echo "path_name, stripped of prefixes = $t"
26 # Same effect as t=`basename $path_name` in this particular case.
27 # t=${path_name%/}; t=${t##*/} is a more general solution, but still fails sometimes.
28 # If $path_name ends with a newline, then `basename $path_name` will not work,
29 # but the above expression will.
30 # (Thanks Stephane Chazelas.)
31
32 t=${path_name%/*.*}
33 # Same effect as t=`dirname $path_name`
34 echo "path_name, stripped of suffixes = $t"
35 # These will fail in some cases, such as "../", "/foo////", # "foo/", "/".
36 # Removing suffixes, especially when the basename has no suffix,
37 # but the dirname does, also complicates matters.
38 # (Thanks, Stephane Chazelas.)
39
40 echo
41
42 t=${path_name:11}
43 echo "$path_name, with first 11 chars stripped off = $t"
44 t=${path_name:11:5}
45 echo "$path_name, with first 11 chars stripped off, length 5 = $t"
46
47 echo
48
49 t=${path_name/bozo/clown}
50 echo "$path_name with \"bozo\" replaced by \"clown\" = $t"
51 t=${path_name/today/}
52 echo "$path_name with \"today\" deleted = $t"
53 t=${path_name//o/O}
54 echo "$path_name with all o's capitalized = $t"
55 t=${path_name//o/}
56 echo "$path_name with all o's deleted = $t"
57
58 exit 0 |
Notes
| [1] | If $parameter is null in a non-interactive script, it will terminate with a 127 exit status (the Bash error code code for "command not found"). |
