| Advanced Bash-Scripting Guide: An in-depth exploration of the art of shell scripting | ||
|---|---|---|
| Prev | Chapter 34. Miscellany | Next |
To keep a record of which user scripts have run during a particular sesssion or over a number of sessions, add the following lines to each script you want to keep track of. This will keep a continuing file record of the script names and invocation times.
1 # Append (>>) following to end of each script tracked. 2 3 date>> $SAVE_FILE #Date and time. 4 echo $0>> $SAVE_FILE #Script name. 5 echo>> $SAVE_FILE #Blank line as separator. 6 7 # Of course, SAVE_FILE defined and exported as environmental variable in ~/.bashrc 8 # (something like ~/.scripts-run) |
The >> operator appends lines to a file. What if you wish to prepend a line to an existing file, that is, to paste it in at the beginning?
1 file=data.txt 2 title="***This is the title line of data text file***" 3 4 echo $title | cat - $file >$file.new 5 # "cat -" concatenates stdout to $file. 6 # End result is 7 #+ to write a new file with $title appended at *beginning*. |
Of course, sed can also do this.
A shell script may act as an embedded command inside another shell script, a Tcl or wish script, or even a Makefile. It can be invoked as as an external shell command in a C program using the system() call, i.e., system("script_name");.
Put together files containing your favorite and most useful definitions and functions. As necessary, "include" one or more of these "library files" in scripts with either the dot (.) or source command.
1 # SCRIPT LIBRARY
2 # ------ -------
3
4 # Note:
5 # No "#!" here.
6 # No "live code" either.
7
8
9 # Useful variable definitions
10
11 ROOT_UID=0 # Root has $UID 0.
12 E_NOTROOT=101 # Not root user error.
13 MAXRETVAL=256 # Maximum (positive) return value of a function.
14 SUCCESS=0
15 FAILURE=-1
16
17
18
19 # Functions
20
21 Usage () # "Usage:" message.
22 {
23 if [ -z "$1" ] # No arg passed.
24 then
25 msg=filename
26 else
27 msg=$@
28 fi
29
30 echo "Usage: `basename $0` "$msg""
31 }
32
33
34 Check_if_root () # Check if root running script.
35 { # From "ex39.sh" example.
36 if [ "$UID" -ne "$ROOT_UID" ]
37 then
38 echo "Must be root to run this script."
39 exit $E_NOTROOT
40 fi
41 }
42
43
44 CreateTempfileName () # Creates a "unique" temp filename.
45 { # From "ex51.sh" example.
46 prefix=temp
47 suffix=`eval date +%s`
48 Tempfilename=$prefix.$suffix
49 }
50
51
52 isalpha2 () # Tests whether *entire string* is alphabetic.
53 { # From "isalpha.sh" example.
54 [ $# -eq 1 ] || return $FAILURE
55
56 case $1 in
57 *[!a-zA-Z]*|"") return $FAILURE;;
58 *) return $SUCCESS;;
59 esac # Thanks, S.C.
60 }
61
62
63 abs () # Absolute value.
64 { # Caution: Max return value = 256.
65 E_ARGERR=-999999
66
67 if [ -z "$1" ] # Need arg passed.
68 then
69 return $E_ARGERR # Obvious error value returned.
70 fi
71
72 if [ "$1" -ge 0 ] # If non-negative,
73 then #
74 absval=$1 # stays as-is.
75 else # Otherwise,
76 let "absval = (( 0 - $1 ))" # change sign.
77 fi
78
79 return $absval
80 }
81
82
83 tolower () # Converts string(s) passed as argument(s)
84 { #+ to lowercase.
85
86 if [ -z "$1" ] # If no argument(s) passed,
87 then #+ send error message
88 echo "(null)" #+ (C-style void-pointer error message)
89 return #+ and return from function.
90 fi
91
92 echo "$@" | tr A-Z a-z
93 # Translate all passed arguments ($@).
94
95 return
96
97 # Use command substitution to set a variable to function output.
98 # For example:
99 # oldvar="A seT of miXed-caSe LEtTerS"
100 # newvar=`tolower "$oldvar"`
101 # echo "$newvar" # a set of mixed-case letters
102 #
103 # Exercise: Rewrite this function to change lowercase passed argument(s)
104 # to uppercase ... toupper() [easy].
105 } |
Use special-purpose comment headers to increase clarity and legibility in scripts.
1 ## Caution. 2 rm -rf *.zzy ## The "-rf" options to "rm" are very dangerous, 3 ##+ especially with wildcards. 4 5 #+ Line continuation. 6 # This is line 1 7 #+ of a multi-line comment, 8 #+ and this is the final line. 9 10 #* Note. 11 12 #o List item. 13 14 #> Another point of view. 15 while [ "$var1" != "end" ] #> while test "$var1" != "end" |
Using the $? exit status variable, a script may test if a parameter contains only digits, so it can be treated as an integer.
1 #!/bin/bash 2 3 SUCCESS=0 4 E_BADINPUT=65 5 6 test "$1" -ne 0 -o "$1" -eq 0 2>/dev/null 7 # An integer is either equal to 0 or not equal to 0. 8 # 2>/dev/null suppresses error message. 9 10 if [ $? -ne "$SUCCESS" ] 11 then 12 echo "Usage: `basename $0` integer-input" 13 exit $E_BADINPUT 14 fi 15 16 let "sum = $1 + 25" # Would give error if $1 not integer. 17 echo "Sum = $sum" 18 19 # Any variable, not just a command line parameter, can be tested this way. 20 21 exit 0 |
The 0 - 255 range for function return values is a severe limitation. Global variables and other workarounds are often problematic. An alternative method for a function to communicate a value back to the main body of the script is to have the function write to stdout the "return value", and assign this to a variable.
Example 34-6. Return value trickery
1 #!/bin/bash
2 # multiplication.sh
3
4 multiply () # Multiplies params passed.
5 {
6
7 local product=1
8
9 until [ -z "$1" ] # Until uses up arguments passed...
10 do
11 let "product *= $1"
12 shift
13 done
14
15 echo $product # Will not echo to stdout,
16 } #+ since this will be assigned to a variable.
17
18 val1=`multiply 15383 25211`
19 echo "val1 = $val1" # 387820813
20
21 val2=`multiply 25 5 20`
22 echo "val2 = $val2" # 2500
23
24 val3=`multiply 188 37 25 47`
25 echo "val3 = $val3" # 8173300
26
27 exit 0 |
The same technique also works for alphanumeric strings. This means that a function can "return" a non-numeric value.
1 capitalize_ichar () # Capitalizes initial character
2 { #+ of argument string(s) passed.
3
4 string0="$@" # Accepts multiple arguments.
5
6 firstchar=${string0:0:1} # First character.
7 string1=${string0:1} # Rest of string(s).
8
9 FirstChar=`echo "$firstchar" | tr a-z A-Z`
10 # Capitalize first character.
11
12 echo "$FirstChar$string1" # Output to stdout.
13
14 }
15
16 newstring=`capitalize_ichar "each sentence should start with a capital letter."`
17 echo "$newstring" # Each sentence should start with a capital letter. |
It is even possible for a function to "return" multiple values with this method.
Example 34-7. Even more return value trickery
1 #!/bin/bash
2 # sum-product.sh
3 # A function may "return" more than one value.
4
5 sum_and_product () # Calculates both sum and product of passed args.
6 {
7 echo $(( $1 + $2 )) $(( $1 * $2 ))
8 # Echoes to stdout each calculated value, separated by space.
9 }
10
11 echo
12 echo "Enter first number "
13 read first
14
15 echo
16 echo "Enter second number "
17 read second
18 echo
19
20 retval=`sum_and_product $first $second` # Assigns output of function.
21 sum=`echo "$retval" | awk '{print $1}'` # Assigns first field.
22 product=`echo "$retval" | awk '{print $2}'` # Assigns second field.
23
24 echo "$first + $second = $sum"
25 echo "$first * $second = $product"
26 echo
27
28 exit 0 |
Using the double parentheses construct, it is possible to use C-like syntax for setting and incrementing variables and in for and while loops. See Example 10-11 and Example 10-16.
A useful scripting technique is to repeatedly feed the output of a filter (by piping) back to the same filter, but with a different set of arguments and/or options. Especially suitable for this is tr.
1 # From "wstrings.sh" example. 2 3 wlist=`strings "$1" | tr A-Z a-z | tr '[:space:]' Z | \ 4 tr -cs '[:alpha:]' Z | tr -s '\173-\377' Z | tr Z ' '` |
The run-parts command is handy for running a set of command scripts in sequence, particularly in combination with cron or at.
It would be nice to be able to invoke X-Windows widgets from a shell script. There happen to exist several packages that purport to do so, namely Xscript, Xmenu, and widtools. The first two of these no longer seem to be maintained. Fortunately, it is still possible to obtain widtools here.
![]() | The widtools (widget tools) package requires the XForms library to be installed. Additionally, the Makefile needs some judicious editing before the package will build on a typical Linux system. Finally, three of the six widgets offered do not work (and, in fact, segfault). |
For more effective scripting with widgets, try Tk or wish (Tcl derivatives), PerlTk (Perl with Tk extensions), tksh (ksh with Tk extensions), XForms4Perl (Perl with XForms extensions), Gtk-Perl (Perl with Gtk extensions), or PyQt (Python with Qt extensions).