- •Table of Contents
- •Chapter 1. Why Shell Programming?
- •2.1. Invoking the script
- •2.2. Preliminary Exercises
- •Part 2. Basics
- •Chapter 3. Exit and Exit Status
- •Chapter 4. Special Characters
- •Chapter 5. Introduction to Variables and Parameters
- •5.1. Variable Substitution
- •5.2. Variable Assignment
- •5.3. Bash Variables Are Untyped
- •5.4. Special Variable Types
- •Chapter 6. Quoting
- •Chapter 7. Tests
- •7.1. Test Constructs
- •7.2. File test operators
- •7.3. Comparison operators (binary)
- •7.4. Nested if/then Condition Tests
- •7.5. Testing Your Knowledge of Tests
- •Chapter 8. Operations and Related Topics
- •8.1. Operators
- •8.2. Numerical Constants
- •Part 3. Beyond the Basics
- •Chapter 9. Variables Revisited
- •9.1. Internal Variables
- •9.2. Manipulating Strings
- •9.2.1. Manipulating strings using awk
- •9.2.2. Further Discussion
- •9.3. Parameter Substitution
- •9.4. Typing variables: declare or typeset
- •9.5. Indirect References to Variables
- •9.6. $RANDOM: generate random integer
- •9.7. The Double Parentheses Construct
- •Chapter 10. Loops and Branches
- •10.1. Loops
- •10.2. Nested Loops
- •10.3. Loop Control
- •10.4. Testing and Branching
- •Chapter 11. Internal Commands and Builtins
- •11.1. Job Control Commands
- •Chapter 12. External Filters, Programs and Commands
- •12.1. Basic Commands
- •12.2. Complex Commands
- •12.3. Time / Date Commands
- •12.4. Text Processing Commands
- •12.5. File and Archiving Commands
- •12.6. Communications Commands
- •12.7. Terminal Control Commands
- •12.8. Math Commands
- •12.9. Miscellaneous Commands
- •Chapter 13. System and Administrative Commands
- •Chapter 14. Command Substitution
- •Chapter 15. Arithmetic Expansion
- •Chapter 16. I/O Redirection
- •16.1. Using exec
- •16.2. Redirecting Code Blocks
- •16.3. Applications
- •Chapter 17. Here Documents
- •Chapter 18. Recess Time
- •Part 4. Advanced Topics
- •Chapter 19. Regular Expressions
- •19.1. A Brief Introduction to Regular Expressions
- •19.2. Globbing
- •Chapter 20. Subshells
- •Chapter 21. Restricted Shells
- •Chapter 22. Process Substitution
- •Chapter 23. Functions
- •23.1. Complex Functions and Function Complexities
- •23.2. Local Variables
- •23.2.1. Local variables make recursion possible.
- •Chapter 24. Aliases
- •Chapter 25. List Constructs
- •Chapter 26. Arrays
- •Chapter 27. Files
- •Chapter 28. /dev and /proc
- •28.2. /proc
- •Chapter 29. Of Zeros and Nulls
- •Chapter 30. Debugging
- •Chapter 31. Options
- •Chapter 32. Gotchas
- •Chapter 33. Scripting With Style
- •33.1. Unofficial Shell Scripting Stylesheet
- •Chapter 34. Miscellany
- •34.2. Shell Wrappers
- •34.3. Tests and Comparisons: Alternatives
- •34.4. Optimizations
- •34.5. Assorted Tips
- •34.6. Oddities
- •34.7. Portability Issues
- •34.8. Shell Scripting Under Windows
- •Chapter 35. Bash, version 2
- •Chapter 36. Endnotes
- •36.1. Author's Note
- •36.2. About the Author
- •36.3. Tools Used to Produce This Book
- •36.3.1. Hardware
- •36.3.2. Software and Printware
- •36.4. Credits
- •Bibliography
- •Appendix A. Contributed Scripts
- •Appendix C. Exit Codes With Special Meanings
- •Appendix D. A Detailed Introduction to I/O and I/O Redirection
- •Appendix E. Localization
- •Appendix F. History Commands
- •Appendix G. A Sample .bashrc File
- •Appendix H. Converting DOS Batch Files to Shell Scripts
- •Appendix I. Exercises
- •Appendix J. Copyright
Advanced Bash−Scripting Guide
Appendix C. Exit Codes With Special Meanings
Table C−1. "Reserved" Exit Codes
Exit Code Number |
Meaning |
Example |
Comments |
|
|
|
|
1 |
catchall for general errors |
let "var1 = 1/0" |
miscellaneous errors, such |
|
|
|
as "divide by zero" |
|
|
|
|
2 |
misuse of shell builtins, |
|
Seldom seen, usually |
|
according to Bash |
|
defaults to exit code 1 |
|
documentation |
|
|
|
|
|
|
126 |
command invoked cannot |
|
permission problem or |
|
execute |
|
command is not an |
|
|
|
executable |
|
|
|
|
127 |
"command not found" |
|
possible problem with |
|
|
|
$PATH or a typo |
|
|
|
|
128 |
invalid argument to exit |
exit 3.14159 |
exit takes only integer args |
|
|
|
in the range 0 − 255 |
|
|
|
|
128+n |
fatal error signal "n" |
kill −9 $PPIDof script |
$? returns 137 (128 + 9) |
|
|
|
|
130 |
script terminated by |
|
Control−C is fatal error |
|
Control−C |
|
signal 2, (130 = 128 + 2, |
|
|
|
see above) |
|
|
|
|
255 |
exit status out of range |
exit −1 |
exit takes only integer args |
|
|
|
in the range 0 − 255 |
|
|
|
|
According to the table, exit codes 1 − 2, 126 − 165, and 255 have special meanings, and should therefore be avoided as user−specified exit parameters. Ending a script with exit 127 would certainly cause confusion when troubleshooting (is the error a "command not found" or a user−defined one?). However, many scripts use an exit 1 as a general bailout upon error. Since exit code 1 signifies so many possible errors, this might not add any additional ambiguity, but, on the other hand, it probably would not be very informative either.
There has been an attempt to systematize exit status numbers (see /usr/include/sysexits.h), but this is intended mostly for C and C++ programmers. It would be well to support a similar standard for scripts. The author of this document proposes restricting user−defined exit codes to the range 64 − 113 (in addition to 0, for success), to conform with the C/C++ standard. This would still leave 50 valid codes, and make troubleshooting scripts more straightforward.
All user−defined exit codes in the accompanying examples to this document now conform to this standard, except where overriding circumstances exist, as in Example 9−2.
Issuing a $? from the command line after a shell script exits gives results consistent with the table above only from the Bash or sh prompt. Running the C−shell or tcsh may give different values in some cases.
Appendix C. Exit Codes With Special Meanings |
341 |
Advanced Bash−Scripting Guide
Appendix D. A Detailed Introduction to I/O and I/O Redirection
written by Stephane Chazelas, and revised by the document author
A command expects the first three file descriptors to be available. The first, fd 0 (standard input, stdin), is for reading. The other two (fd 1, stdout and fd 2, stderr) are for writing.
There is a stdin, stdout, and a stderr associated with each command. ls 2>&1 means temporarily connecting the stderr of the ls command to the same "resource" as the shell's stdout.
By convention, a command reads its input from fd 0 (stdin), prints normal output to fd 1 (stdout), and error ouput to fd 2 (stderr). If one of those three fd's is not open, you may encounter problems:
bash$ cat /etc/passwd >&−
cat: standard output: Bad file descriptor
For example, when xterm runs, it first initializes itself. Before running the user's shell, xterm opens the terminal device (/dev/pts/<n> or something similar) three times.
At this point, Bash inherits these three file descriptors, and each command (child process) run by Bash inherits them in turn, except when you redirect the command. Redirection means reassigning one of the file descriptors to another file (or a pipe, or anything permissable). File descriptors may be reassigned locally (for a command, a command group, a subshell, a while or if or case or for loop...), or globally, for the remainder of the shell (using exec).
ls > /dev/null means |
running ls with its fd 1 connected to /dev/null. |
||||||||
|
|
|
|
|
|||||
bash$ lsof −a −p $$ −d0,1,2 |
|
|
|
|
|||||
COMMAND PID |
USER |
FD |
TYPE DEVICE SIZE NODE NAME |
||||||
bash |
363 |
bozo |
|
|
0u |
CHR |
136,1 |
3 |
/dev/pts/1 |
bash |
363 |
bozo |
|
|
1u |
CHR |
136,1 |
3 |
/dev/pts/1 |
bash |
363 |
bozo |
|
|
2u |
CHR |
136,1 |
3 |
/dev/pts/1 |
bash$ exec 2> /dev/null |
|
|
|
|
|
|
|||
bash$ lsof −a −p $$ −d0,1,2 |
|
|
|
|
|||||
COMMAND PID |
USER |
FD |
TYPE DEVICE SIZE NODE NAME |
||||||
bash |
371 |
bozo |
|
|
0u |
CHR |
136,1 |
3 |
/dev/pts/1 |
bash |
371 |
bozo |
|
|
1u |
CHR |
136,1 |
3 |
/dev/pts/1 |
bash |
371 |
bozo |
|
|
2w |
CHR |
1,3 |
120 |
/dev/null |
bash$ bash −c 'lsof −a −p $$ −d0,1,2' | cat |
|
||||||||
COMMAND PID USER |
FD |
TYPE DEVICE SIZE NODE NAME |
|
||||||
lsof |
379 |
root |
0u |
|
CHR |
136,1 |
3 /dev/pts/1 |
||
lsof |
379 |
root |
1w |
|
FIFO |
|
0,0 |
7118 pipe |
|
lsof |
379 |
root |
2u |
|
CHR |
136,1 |
3 /dev/pts/1 |
bash$ echo "$(bash −c 'lsof −a −p $$ −d0,1,2' 2>&1)"
Appendix D. A Detailed Introduction to I/O and I/O Redirection |
342 |
Advanced Bash−Scripting Guide
COMMAND PID USER |
FD |
TYPE DEVICE SIZE NODE NAME |
|||||
lsof |
426 |
root |
0u |
CHR |
136,1 |
3 |
/dev/pts/1 |
lsof |
426 |
root |
1w |
FIFO |
0,0 |
7520 |
pipe |
lsof |
426 |
root |
2w |
FIFO |
0,0 |
7520 |
pipe |
|
|
|
|
|
|
|
|
This works for different types of redirection.
Exercise: Analyze the following script.
#! /usr/bin/env bash
mkfifo /tmp/fifo1 /tmp/fifo2
while read a; do echo "FIFO1: $a"; done < /tmp/fifo1 & exec 7> /tmp/fifo1
exec 8> >(while read a; do echo "FD8: $a, to fd7"; done >&7)
exec 3>&1
(
(
(
while read a; do echo "FIFO2: $a"; done < /tmp/fifo2 | tee /dev/stderr | tee /dev/fd/4 | tee / exec 3> /tmp/fifo2
echo 1st, to stdout sleep 1
echo 2nd, to stderr >&2 sleep 1
echo 3rd, to fd 3 >&3 sleep 1
echo 4th, to fd 4 >&4 sleep 1
echo 5th, to fd 5 >&5 sleep 1
echo 6th, through a pipe | sed 's/.*/PIPE: &, to fd 5/' >&5 sleep 1
echo 7th, to fd 6 >&6 sleep 1
echo 8th, to fd 7 >&7 sleep 1
echo 9th, to fd 8 >&8
) 4>&1 >&3 3>&− | while read a; do echo "FD4: $a"; done 1>&3 5>&− 6>&− ) 5>&1 >&3 | while read a; do echo "FD5: $a"; done 1>&3 6>&−
) 6>&1 >&3 | while read a; do echo "FD6: $a"; done 3>&−
rm −f /tmp/fifo1 /tmp/fifo2
# For each command and subshell, figure out which fd points to what.
exit 0
Appendix E. Localization
Localization is an undocumented Bash feature.
Appendix E. Localization |
343 |
Advanced Bash−Scripting Guide
A localized shell script echoes its text output in the language defined as the system's locale. A Linux user in Berlin, Germany, would get script output in German, whereas his cousin in Berlin, Maryland, would get output from the same script in English.
To create a localized script, use the following template to write all messages to the user (error messages, prompts, etc.).
#!/bin/bash
# localized.sh
E_CDERROR=65
error()
{
printf "$@" >&2 exit $E_CDERROR
}
cd $var || error $"Can't cd to %s." "$var" read −p $"Enter the value: " var
# ...
bash$ bash −D localized.sh
"Can't cd to %s." "Enter the value: "
This lists all the localized text. (The −D option lists double−quoted strings prefixed by a $, without executing the script.)
bash$ bash −−dump−po−strings localized.sh
#: a:6
msgid "Can't cd to %s." msgstr ""
#: a:7
msgid "Enter the value: " msgstr ""
The −−dump−po−strings option to Bash resembles the −D option, but uses gettext "po" format.
Now, build a language.po file for each language that the script will be translated into, specifying the msgstr. As an example:
fr.po:
#: a:6
msgid "Can't cd to %s."
msgstr "Impossible de se positionner dans le répertoire %s." #: a:7
msgid "Enter the value: " msgstr "Entrez la valeur : "
Then, run msgfmt.
msgfmt −o localized.sh.mo fr.po
Place the resulting localized.sh.mo file in the /usr/local/share/locale/fr/LC_MESSAGES directory, and at the beginning of the script, insert
Appendix E. Localization |
344 |