
- •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

Chapter 16. I/O Redirection
There are always three default "files" open, stdin (the keyboard), stdout (the screen), and stderr (error messages output to the screen). These, and any other open files, can be redirected.
Redirection simply means capturing output from a file, command, program, script, or even code block within a script (see Example 4−1 and Example 4−2) and sending it as input to another file, command, program, or script.
Each open file gets assigned a file descriptor. [44] The file descriptors for stdin, stdout, and stderr are 0, 1, and 2, respectively. For opening additional files, there remain descriptors 3 to 9. It is sometimes useful to assign one of these additional file descriptors to stdin, stdout, or stderr as a
temporary duplicate link. [45] This simplifies restoration to normal after complex redirection and reshuffling (see Example 16−1).
>
#Redirect stdout to a file.
#Creates the file if not present, otherwise overwrites it.
ls −lR > dir−tree.list
#Creates a file containing a listing of the directory tree.
:> filename
#The > truncates file "filename" to zero length.
#If file not present, creates zero−length file (same effect as 'touch').
#The : serves as a dummy placeholder, producing no output.
>>
#Redirect stdout to a file.
#Creates the file if not present, otherwise appends to it.
#Single−line redirection commands (affect only the line they are on):
#−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
1>filename
#Redirect stdout to file "filename".
1>>filename
#Redirect and append stdout to file "filename". 2>filename
#Redirect stderr to file "filename". 2>>filename
#Redirect and append stderr to file "filename".
#==============================================================================
# Redirecting stdout, one line at a time. LOGFILE=script.log
echo "This statement is sent to the log file, \"$LOGFILE\"." 1>$LOGFILE echo "This statement is appended to \"$LOGFILE\"." 1>>$LOGFILE
echo "This statement is also appended to \"$LOGFILE\"." 1>>$LOGFILE
echo "This statement is echoed to stdout, and will not appear in \"$LOGFILE\"."
#These redirection commands automatically "reset" after each line.
#Redirecting stderr, one line at a time.
Chapter 16. I/O Redirection |
218 |

Advanced Bash−Scripting Guide
ERRORFILE=script.errors |
|
|
bad_command1 2>$ERRORFILE |
# |
Error message sent to $ERRORFILE. |
bad_command2 2>>$ERRORFILE |
# |
Error message appended to $ERRORFILE. |
bad_command3 |
# |
Error message echoed to stderr, |
|
#+ |
and does not appear in $ERRORFILE. |
# These redirection commands also automatically "reset" after each line. #==============================================================================
2>&1
#Redirects stderr to stdout.
#Error messages get sent to same place as standard output.
i>&j
#Redirects file descriptor i to j.
#All output of file pointed to by i gets sent to file pointed to by j.
>&j
#Redirects, by default, file descriptor 1 (stdout) to j.
#All stdout gets sent to file pointed to by j.
0<
<
#Accept input from a file.
#Companion command to ">", and often used in combination with it.
#grep search−word <filename
[j]<>filename
#Open file "filename" for reading and writing, and assign file descriptor "j" to it.
#If "filename" does not exist, create it.
#If file descriptor "j" is not specified, default to fd 0, stdin.
# |
|
|
# An application of this is |
writing at a specified place in a file. |
|
echo 1234567890 > File |
# |
Write string to "File". |
exec 3<> File |
# |
Open "File" and assign fd 3 to it. |
read −n 4 <&3 |
# |
Read only 4 characters. |
echo −n . >&3 |
# |
Write a decimal point there. |
exec 3>&− |
# |
Close fd 3. |
cat File |
# |
==> 1234.67890 |
# Random access, by golly. |
|
|
|
#Pipe.
#General purpose process and command chaining tool.
#Similar to ">", but more general in effect.
#Useful for chaining commands, scripts, files, and programs together. cat *.txt | sort | uniq > result−file
#Sorts the output of all the .txt files and deletes duplicate lines,
#finally saves results to "result−file".
Multiple instances of input and output redirection and/or pipes can be combined in a single command line.
command < input−file > output−file
command1 | command2 | command3 > output−file
Chapter 16. I/O Redirection |
219 |

Advanced Bash−Scripting Guide
See Example 12−23 and Example A−10.
Multiple output streams may be redirected to one file.
ls −yz >> command.log 2>&1
#Capture result of illegal options "yz" to "ls" in file "command.log".
#Because stderr redirected to the file, any error messages will also be there.
Closing File Descriptors
n<&−
Close input file descriptor n.
0<&−, <&−
Close stdin.
n>&−
Close output file descriptor n.
1>&−, >&−
Close stdout.
Child processes inherit open file descriptors. This is why pipes work. To prevent an fd from being inherited, close it.
# |
Redirecting only stderr to a |
pipe. |
|
|
exec |
3>&1 |
|
# Save current "value" of stdout. |
|
ls −l 2>&1 >&3 3>&− | grep bad |
3>&− |
# Close fd 3 for 'ls' and 'grep'. |
||
exec |
3>&− |
|
# Now close it for the remainder of the script. |
|
# |
Thanks, S.C. |
|
|
For a more detailed introduction to I/O redirection see Appendix D.
16.1. Using exec
The exec <filename command redirects stdin to a file. From that point on, all stdin comes from that file, rather than its normal source (usually keyboard input). This provides a method of reading a file line by line and possibly parsing each line of input using sed and/or awk.
Example 16−1. Redirecting stdin using exec
#!/bin/bash
# Redirecting stdin using 'exec'.
16.1. Using exec |
220 |

|
|
|
Advanced Bash−Scripting Guide |
exec 6<&0 |
# Link file descriptor #6 with stdin. |
||
exec < data−file |
# stdin replaced by file "data−file" |
||
read |
a1 |
# Reads |
first line of file "data−file". |
read |
a2 |
# Reads |
second line of file "data−file." |
echo
echo "Following lines read from file." echo "−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−" echo $a1
echo $a2
echo; echo; echo
exec 0<&6 6<&−
#Now restore stdin from fd #6, where it had been saved,
#and close fd #6 ( 6<&− ) to free it for other processes to use.
# <&6 6<&− also works.
echo −n "Enter data "
read b1 # Now "read" functions as expected, reading from normal stdin. echo "Input read from stdin."
echo "−−−−−−−−−−−−−−−−−−−−−−" echo "b1 = $b1"
echo
exit 0
16.2. Redirecting Code Blocks
Blocks of code, such as while, until, and for loops, even if/then test blocks can also incorporate redirection of stdin. Even a function may use this form of redirection (see Example 23−7). The < operator at the the end of the code block accomplishes this.
Example 16−2. Redirected while loop
#!/bin/bash
if [ −z "$1" ] then
Filename=names.data # Default, if no filename specified. else
Filename=$1
fi
#Filename=${1:−names.data}
#can replace the above test (parameter substitution).
count=0 |
|
echo |
|
while [ "$name" != Smith ] |
# Why is variable $name in quotes? |
do |
|
read name |
# Reads from $Filename, rather than stdin. |
16.2. Redirecting Code Blocks |
221 |

|
|
Advanced Bash−Scripting Guide |
echo $name |
|
|
let "count += 1" |
|
|
done |
<"$Filename" |
# Redirects stdin to file $Filename. |
# |
^^^^^^^^^^^^ |
|
echo; echo "$count names read"; echo
#Note that in some older shell scripting languages,
#the redirected loop would run as a subshell.
#Therefore, $count would return 0, the initialized value outside the loop.
#Bash and ksh avoid starting a subshell whenever possible,
#so that this script, for example, runs correctly.
#Thanks to Heiner Steven for pointing this out.
exit 0
Example 16−3. Alternate form of redirected while loop
#!/bin/bash
#This is an alternate form of the preceding script.
#Suggested by Heiner Steven
#as a workaround in those situations when a redirect loop
#runs as a subshell, and therefore variables inside the loop
#do not keep their values upon loop termination.
if [ −z "$1" ] then
Filename=names.data # Default, if no filename specified. else
Filename=$1
fi
exec |
3<&0 |
# |
Save stdin to file descriptor 3. |
exec |
0<"$Filename" |
# |
Redirect standard input. |
count=0 echo
while [ "$name" != Smith ] do
read name |
# Reads from redirected stdin ($Filename). |
|
echo $name |
|
|
let "count += 1" |
|
|
done |
<"$Filename" |
# Loop reads from file $Filename. |
# |
^^^^^^^^^^^^ |
|
exec |
0<&3 |
# Restore old stdin. |
exec |
3<&− |
# Close temporary fd 3. |
echo; echo "$count names read"; echo |
||
exit |
0 |
|
Example 16−4. Redirected until loop
16.2. Redirecting Code Blocks |
222 |

Advanced Bash−Scripting Guide
#!/bin/bash
# Same as previous example, but with "until" loop.
if [ −z "$1" ] |
|
then |
|
Filename=names.data |
# Default, if no filename specified. |
else |
|
Filename=$1 |
|
fi |
|
# while [ "$name" != Smith ] |
|
until [ "$name" = Smith ] |
# Change != to =. |
do |
|
read name |
# Reads from $Filename, rather than stdin. |
echo $name |
|
done <"$Filename" |
# Redirects stdin to file $Filename. |
#^^^^^^^^^^^^
#Same results as with "while" loop in previous example.
exit 0
Example 16−5. Redirected for loop
#!/bin/bash
if [ −z "$1" ] then
Filename=names.data # Default, if no filename specified. else
Filename=$1
fi
line_count=`wc $Filename | awk '{ print $1 }'` # Number of lines in target file.
#Very contrived and kludgy, nevertheless shows that
#it's possible to redirect stdin within a "for" loop...
#if you're clever enough.
# |
|
|
# |
More concise is |
line_count=$(wc < "$Filename") |
for name in `seq $line_count`
#while [ "$name" != Smith ]
do
read name echo $name
if [ "$name" = Smith ] then
break
fi
done <"$Filename"
#^^^^^^^^^^^^
# Recall that "seq" prints |
sequence of numbers. |
|
−− more complicated than |
a "while" loop |
−− |
#Reads from $Filename, rather than stdin.
#Need all this extra baggage here.
#Redirects stdin to file $Filename.
exit 0
We can modify the previous example to also redirect the output of the loop.
Example 16−6. Redirected for loop (both stdin and stdout redirected)
16.2. Redirecting Code Blocks |
223 |

|
Advanced Bash−Scripting Guide |
#!/bin/bash |
|
if [ −z "$1" ] |
|
then |
|
Filename=names.data |
# Default, if no filename specified. |
else |
|
Filename=$1 |
|
fi |
|
Savefile=$Filename.new |
# Filename to save results in. |
FinalName=Jonah |
# Name to terminate "read" on. |
line_count=`wc $Filename | awk '{ print $1 }'` # Number of lines in target file.
for name in `seq $line_count` do
read |
name |
|
|
echo |
"$name" |
|
|
if |
[ |
"$name" = "$FinalName" ] |
|
then |
|
|
|
|
break |
|
|
fi |
|
|
|
done |
< |
"$Filename" > "$Savefile" |
# Redirects stdin to file $Filename, |
# |
^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
and saves it to backup file. |
|
exit |
0 |
|
|
Example 16−7. Redirected if/then test
#!/bin/bash
if [ −z "$1" ] then
Filename=names.data # Default, if no filename specified. else
Filename=$1
fi
TRUE=1 |
|
|
|
|
if [ "$TRUE" ] |
# if true |
and |
if : |
also work. |
then |
|
|
|
|
read name |
|
|
|
|
echo $name |
|
|
|
|
fi <"$Filename" |
|
|
|
|
#^^^^^^^^^^^^
#Reads only first line of file.
#An "if/then" test has no way of iterating unless embedded in a loop.
exit 0
Redirecting the stdout of a code block has the effect of saving its output to a file. See Example 4−2.
Here documents are a special case of redirected code blocks.
16.2. Redirecting Code Blocks |
224 |