| Advanced Bash-Scripting Guide: A complete guide to shell scripting, using Bash | ||
|---|---|---|
| Prev | Chapter 3. Tutorial / Reference | Next |
3.32. Miscellany
Nobody really knows what the Bourne shell's grammar is. Even examination of the source code is little help. | |
| Tom Duff |
3.32.1. Interactive and non-interactive shells and scripts
An interactive shell reads commands from user input on a tty. Among other things, such a shell reads startup files on activation, displays a prompt, and enables job control by default. The user can interact with the shell.
A shell running a script is always a non-interactive shell. All the same, the script can still access its tty. It is even possible to emulate an interactive shell in a script.
1 #!/bin/bash 2 MY_PROMPT='$ ' 3 while : 4 do 5 echo -n "$MY_PROMPT" 6 read line 7 eval "$line" 8 done 9 10 exit 0 11 12 # This example script, and much of the above explanation supplied by 13 # Stephane Chazelas (thanks again). |
Let us consider an interactive script to be one that requires input from the user, usually with read statements (see Example 3-78). "Real life" is actually a bit messier than that. For now, assume an interactive script is bound to a tty, a script that a user has invoked from the console or an xterm.
Init and startup scripts are necessarily non-interactive, since they must run without human intervention. Many administrative and system maintenance scripts are likewise non-interactive. Unvarying repetitive tasks cry out for automation by non-interactive scripts.
Non-interactive scripts can run in the background, but interactive ones hang, waiting for input that never comes. Handle that difficulty by having an expect script or embedded here document feed input to an interactive script running as a background job. In the simplest case, redirect a file to supply input to a read statement (read variable <file). These particular workarounds make possible general purpose scripts that run in either interactive or non-interactive modes.
If a script needs to test whether it is running in an interactive shell, it is simply a matter of finding whether the prompt variable, $PS1 is set. (If the user is being prompted for input, then the script needs to display a prompt.)
1 if [ -z $PS1 ] # no prompt? 2 then 3 # non-interactive 4 ... 5 else 6 # interactive 7 ... 8 fi |
1 case $- in 2 *i*) # interactive shell 3 ;; 4 *) # non-interactive shell 5 ;; 6 # (Thanks to "UNIX F.A.Q.", 1993) |
![]() | Scripts may be forced to run in interactive mode with the -i option or with a #!/bin/bash -i header. Be aware that this can cause erratic script behavior or show error messages even when no error is present. |
3.32.2. Tests and Comparisons
For tests, the [[ ]] construct may be more appropriate than [ ]. Likewise, arithmetic comparisons might benefit from the (( )) construct.
1 a=8 2 3 # All of the comparisons below are equivalent. 4 test "$a" -lt 16 && echo "yes, $a < 16" # "and list" 5 /bin/test "$a" -lt 16 && echo "yes, $a < 16" 6 [ "$a" -lt 16 ] && echo "yes, $a < 16" 7 [[ $a -lt 16 ]] && echo "yes, $a < 16" # Quoting variables within 8 (( a < 16 )) && echo "yes, $a < 16" # [[ ]] and (( )) not necessary. 9 10 city="New York" 11 # Again, all of the comparisons below are equivalent. 12 test "$city" \< Paris && echo "Yes, Paris is greater than $city" # Greater ASCII order. 13 /bin/test "$city" \< Paris && echo "Yes, Paris is greater than $city" 14 [ "$city" \< Paris ] && echo "Yes, Paris is greater than $city" 15 [[ $city < Paris ]] && echo "Yes, Paris is greater than $city" # Need not quote $city. 16 17 # Thank you, S.C. |
3.32.3. Optimizations
Most shell scripts are quick 'n dirty solutions to non-complex problems. As such, optimizing them for speed is not much of an issue. Consider the case, though, where a script carries out an important task, does it well, but runs too slowly. Rewriting it in a compiled language may not be a palatable option. The simplest fix would be to rewrite the parts of the script that slow it down. Is it possible to apply principles of code optimization even to a lowly shell script?
Check the loops in the script. Time consumed by repetitive operations adds up quickly. Use the time and times tools to profile computation-intensive commands. Consider rewriting time-critical code sections in C, or even in assembler.
Try to minimize file i/o. Bash is not particularly efficient at handling files, so consider using more appropriate tools for this within the script, such as awk or Perl.
Try to write your scripts in a structured, coherent form, so they can be reorganized and tightened up as necessary. Some of the optimization techniques applicable to high-level languages may work for scripts, but others, such as loop unrolling, are mostly irrelevant. Above all, use common sense.
3.32.4. Assorted Tips
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)
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 a file of your favorite and most useful definitions and functions, then "include" this file in scripts as necessary with either the dot (.) or source command.
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 3-52 and Example 3-57.
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).

