Initializing or changing the value of a variable
All-purpose assignment operator, which works for both arithmetic and string assignments.
1 var=27 2 category=minerals # No spaces allowed after the "=". |
![]() | Do not confuse the "=" assignment operator with the = test operator.
|
plus
minus
multiplication
division
exponentiation
1 # Bash, version 2.02, introduced the "**" exponentiation operator. 2 3 let "z=5**3" # 5 * 5 * 5 4 echo "z = $z" # z = 125 |
modulo, or mod (returns the remainder of an integer division operation)
bash$ expr 5 % 3 2 |
This operator finds use in, among other things, generating numbers within a specific range (see Example 9-11 and Example 9-15) and formatting program output (see Example 27-16 and Example A-6). It can even be used to generate prime numbers, (see Example A-15). Modulo turns up surprisingly often in numerical recipes.
Example 8-1. Greatest common divisor
1 #!/bin/bash
2 # gcd.sh: greatest common divisor
3 # Uses Euclid's algorithm
4
5 # The "greatest common divisor" (gcd) of two integers
6 #+ is the largest integer that will divide both, leaving no remainder.
7
8 # Euclid's algorithm uses successive division.
9 # In each pass,
10 #+ dividend <--- divisor
11 #+ divisor <--- remainder
12 #+ until remainder = 0.
13 # The gcd = dividend, on the final pass.
14 #
15 # For an excellent discussion of Euclid's algorithm, see
16 #+ Jim Loy's site, http://www.jimloy.com/number/euclids.htm.
17
18
19 # ------------------------------------------------------
20 # Argument check
21 ARGS=2
22 E_BADARGS=85
23
24 if [ $# -ne "$ARGS" ]
25 then
26 echo "Usage: `basename $0` first-number second-number"
27 exit $E_BADARGS
28 fi
29 # ------------------------------------------------------
30
31
32 gcd ()
33 {
34
35 dividend=$1 # Arbitrary assignment.
36 divisor=$2 #! It doesn't matter which of the two is larger.
37 # Why not?
38
39 remainder=1 # If an uninitialized variable is used inside
40 #+ test brackets, an error message results.
41
42 until [ "$remainder" -eq 0 ]
43 do # ^^^^^^^^^^ Must be previously initialized!
44 let "remainder = $dividend % $divisor"
45 dividend=$divisor # Now repeat with 2 smallest numbers.
46 divisor=$remainder
47 done # Euclid's algorithm
48
49 } # Last $dividend is the gcd.
50
51
52 gcd $1 $2
53
54 echo; echo "GCD of $1 and $2 = $dividend"; echo
55
56
57 # Exercises :
58 # ---------
59 # 1) Check command-line arguments to make sure they are integers,
60 #+ and exit the script with an appropriate error message if not.
61 # 2) Rewrite the gcd () function to use local variables.
62
63 exit 0 |
plus-equal (increment
variable by a constant)nbsp;COMMAND.
As a simple example, consider this alternative to the echo-grep construction.
1 # Instead of: 2 if echo "$VAR" | grep -q txt # if [[ $VAR = *txt* ]] 3 # etc. 4 5 # Try: 6 if grep -q "txt" <<< "$VAR" 7 then # ^^^ 8 echo "$VAR contains the substring sequence \"txt\"" 9 fi 10 # Thank you, Sebastian Kaminski, for the suggestion. |
Or, in combination with read:
1 String="This is a string of words."
2
3 read -r -a Words <<< "$String"
4 # The -a option to "read"
5 #+ assigns the resulting values to successive members of an array.
6
7 echo "First word in String is: ${Words[0]}" # This
8 echo "Second word in String is: ${Words[1]}" # is
9 echo "Third word in String is: ${Words[2]}" # a
10 echo "Fourth word in String is: ${Words[3]}" # string
11 echo "Fifth word in String is: ${Words[4]}" # of
12 echo "Sixth word in String is: ${Words[5]}" # words.
13 echo "Seventh word in String is: ${Words[6]}" # (null)
14 # Past end of $String.
15
16 # Thank you, Francisco Lobo, for the suggestion. |
Example 19-13. Prepending a line to a file
1 #!/bin/bash 2 # prepend.sh: Add text at beginning of file. 3 # 4 # Example contributed by Kenny Stauffer, 5 #+ and slightly modified by document author. 6 7 8 E_NOSUCHFILE=85 9 10 read -p "File: " file # -p arg to 'read' displays prompt. 11 if [ ! -e "$file" ] 12 then # Bail out if no such file. 13 echo "File $file not found." 14 exit $E_NOSUCHFILE 15 fi 16 17 read -p "Title: " title 18 cat - $file <<<$title > $file.new 19 20 echo "Modified file is $file.new" 21 22 exit # Ends script execution. 23 24 from 'man bash': 25 Here Strings 26 A variant of here documents, the format is: 27 28 <<<word 29 30 The word is expanded and supplied to the command on its standard input. 31 32 33 Of course, the following also works: 34 sed -e '1i\ 35 Title: ' $file |
Example 19-14. Parsing a mailbox
1 #!/bin/bash
2 # Script by Francisco Lobo,
3 #+ and slightly modified and commented by ABS Guide author.
4 # Used in ABS Guide with permission. (Thank you!)
5
6 # This script will not run under Bash versions < 3.0.
7
8
9 E_MISSING_ARG=67
10 if [ -z "$1" ]
11 then
12 echo "Usage: $0 mailbox-file"
13 exit $E_MISSING_ARG
14 fi
15
16 mbox_grep() # Parse mailbox file.
17 {
18 declare -i body=0 match=0
19 declare -a date sender
20 declare mail header value
21
22
23 while IFS= read -r mail
24 # ^^^^ Reset $IFS.
25 # Otherwise "read" will strip leading & trailing space from its input.
26
27 do
28 if [[ $mail =~ "^From " ]] # Match "From" field in message.
29 then
30 (( body = 0 )) # "Zero out" variables.
31 (( match = 0 ))
32 unset date
33
34 elif (( body ))
35 then
36 (( match ))
37 # echo "$mail"
38 # Uncomment above line if you want entire body of message to display.
39
40 elif [[ $mail ]]; then
41 IFS=: read -r header value <<< "$mail"
42 # ^^^ "here string"
43
44 case "$header" in
45 [Ff][Rr][Oo][Mm] ) [[ $value =~ "$2" ]] && (( match++ )) ;;
46 # Match "From" line.
47 [Dd][Aa][Tt][Ee] ) read -r -a date <<< "$value" ;;
48 # ^^^
49 # Match "Date" line.
50 [Rr][Ee][Cc][Ee][Ii][Vv][Ee][Dd] ) read -r -a sender <<< "$value" ;;
51 # ^^^
52 # Match IP Address (may be spoofed).
53 esac
54
55 else
56 (( body++ ))
57 (( match )) &&
58 echo "MESSAGE ${date:+of: ${date[*]} }"
59 # Entire $date array ^
60 echo "IP address of sender: ${sender[1]}"
61 # Second field of "Received" line ^
62
63 fi
64
65
66 done < "$1" # Redirect stdout of file into loop.
67 }
68
69
70 mbox_grep "$1" # Send mailbox file to function.
71
72 exit $?
73
74 # Exercises:
75 # ---------
76 # 1) Break the single function, above, into multiple functions,
77 #+ for the sake of readability.
78 # 2) Add additional parsing to the script, checking for various keywords.
79
80
81
82 $ mailbox_grep.sh scam_mail
83 MESSAGE of Thu, 5 Jan 2006 08:00:56 -0500 (EST)
84 IP address of sender: 196.3.62.4 |
Exercise: Find other uses for here strings, such as, for example, feeding input to dc.