| Advanced Bash-Scripting Guide: A complete guide to shell scripting, using Bash | ||
|---|---|---|
| Prev | Chapter 16. I/O Redirection | Next | 
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.
  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
#!/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`  # Recall that "seq" prints sequence of numbers.
# while [ "$name" != Smith ]   --   more complicated than a "while" loop   --
do
  read name                    # Reads from $Filename, rather than stdin.
  echo $name
  if [ "$name" = Smith ]       # Need all this extra baggage here.
  then
    break
  fi  
done <"$Filename"              # 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)
#!/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.  |