old-www/LDP/abs/html/redircb.html

666 lines
11 KiB
HTML

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML
><HEAD
><TITLE
>Redirecting Code Blocks</TITLE
><META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.7"><LINK
REL="HOME"
TITLE="Advanced Bash-Scripting Guide"
HREF="index.html"><LINK
REL="UP"
TITLE="I/O Redirection"
HREF="io-redirection.html"><LINK
REL="PREVIOUS"
TITLE="Using exec"
HREF="x17974.html"><LINK
REL="NEXT"
TITLE="Applications"
HREF="redirapps.html"></HEAD
><BODY
CLASS="SECT1"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>Advanced Bash-Scripting Guide: </TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="x17974.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
>Chapter 20. I/O Redirection</TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="redirapps.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="SECT1"
><H1
CLASS="SECT1"
><A
NAME="REDIRCB"
></A
>20.2. Redirecting Code Blocks</H1
><P
><A
NAME="REDIRREF"
></A
>Blocks of code, such as <A
HREF="loops1.html#WHILELOOPREF"
>while</A
>, <A
HREF="loops1.html#UNTILLOOPREF"
>until</A
>, and <A
HREF="loops1.html#FORLOOPREF1"
>for</A
> loops, even <A
HREF="tests.html#IFTHEN"
>if/then</A
> test blocks can also incorporate
redirection of <TT
CLASS="FILENAME"
>stdin</TT
>. Even a function may
use this form of redirection (see <A
HREF="complexfunct.html#REALNAME"
>Example 24-11</A
>).
The <SPAN
CLASS="TOKEN"
>&#60;</SPAN
> operator at the end of the code block
accomplishes this.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="REDIR2"
></A
><P
><B
>Example 20-5. Redirected <I
CLASS="FIRSTTERM"
>while</I
> loop</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# redir2.sh
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 &#60;"$Filename" # Redirects stdin to file $Filename.
# ^^^^^^^^^^^^
echo; echo "$count names read"; echo
exit 0
# 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.)
# However . . .
# Bash *can* sometimes start a subshell in a PIPED "while-read" loop,
#+ as distinct from a REDIRECTED "while" loop.
abc=hi
echo -e "1\n2\n3" | while read l
do abc="$l"
echo $abc
done
echo $abc
# Thanks, Bruno de Oliveira Schneider, for demonstrating this
#+ with the above snippet of code.
# And, thanks, Brian Onn, for correcting an annotation error.</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="EXAMPLE"
><A
NAME="REDIR2A"
></A
><P
><B
>Example 20-6. Alternate form of redirected <I
CLASS="FIRSTTERM"
>while</I
> loop</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/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&#60;&#38;0 # Save stdin to file descriptor 3.
exec 0&#60;"$Filename" # Redirect standard input.
count=0
echo
while [ "$name" != Smith ]
do
read name # Reads from redirected stdin ($Filename).
echo $name
let "count += 1"
done # Loop reads from file $Filename
#+ because of line 20.
# The original version of this script terminated the "while" loop with
#+ done &#60;"$Filename"
# Exercise:
# Why is this unnecessary?
exec 0&#60;&#38;3 # Restore old stdin.
exec 3&#60;&#38;- # Close temporary fd 3.
echo; echo "$count names read"; echo
exit 0</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="EXAMPLE"
><A
NAME="REDIR3"
></A
><P
><B
>Example 20-7. Redirected <I
CLASS="FIRSTTERM"
>until</I
> loop</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/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 &#60;"$Filename" # Redirects stdin to file $Filename.
# ^^^^^^^^^^^^
# Same results as with "while" loop in previous example.
exit 0</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="EXAMPLE"
><A
NAME="REDIR4"
></A
><P
><B
>Example 20-8. Redirected <I
CLASS="FIRSTTERM"
>for</I
> loop</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/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 -l &#60; "$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 &#60;"$Filename" # Redirects stdin to file $Filename.
# ^^^^^^^^^^^^
exit 0</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
>We can modify the previous example to also redirect the output of
the loop.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="REDIR4A"
></A
><P
><B
>Example 20-9. Redirected <I
CLASS="FIRSTTERM"
>for</I
> loop (both
<TT
CLASS="FILENAME"
>stdin</TT
> and <TT
CLASS="FILENAME"
>stdout</TT
>
redirected)</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/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 &#60; "$Filename" &#62; "$Savefile" # Redirects stdin to file $Filename,
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ and saves it to backup file.
exit 0</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="EXAMPLE"
><A
NAME="REDIR5"
></A
><P
><B
>Example 20-10. Redirected <I
CLASS="FIRSTTERM"
>if/then</I
> test</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/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 &#60;"$Filename"
# ^^^^^^^^^^^^
# Reads only first line of file.
# An "if/then" test has no way of iterating unless embedded in a loop.
exit 0</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="EXAMPLE"
><A
NAME="NAMESDATA"
></A
><P
><B
>Example 20-11. Data file <I
CLASS="FIRSTTERM"
>names.data</I
> for above
examples</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>Aristotle
Arrhenius
Belisarius
Capablanca
Dickens
Euler
Goethe
Hegel
Jonah
Laplace
Maroczy
Purcell
Schmidt
Schopenhauer
Semmelweiss
Smith
Steinmetz
Tukhashevsky
Turing
Venn
Warshawski
Znosko-Borowski
# This is a data file for
#+ "redir2.sh", "redir3.sh", "redir4.sh", "redir4a.sh", "redir5.sh".</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
>Redirecting the <TT
CLASS="FILENAME"
>stdout</TT
> of a code
block has the effect of saving its output to a file. See <A
HREF="special-chars.html#RPMCHECK"
>Example 3-2</A
>.</P
><P
><A
HREF="here-docs.html#HEREDOCREF"
>Here documents</A
>
are a special case of redirected code blocks. That being the case,
it should be possible to feed the output of a <I
CLASS="FIRSTTERM"
>here
document</I
> into the <TT
CLASS="FILENAME"
>stdin</TT
> for a
<I
CLASS="FIRSTTERM"
>while loop</I
>.</P
><P
> <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
># This example by Albert Siersema
# Used with permission (thanks!).
function doesOutput()
# Could be an external command too, of course.
# Here we show you can use a function as well.
{
ls -al *.jpg | awk '{print $5,$9}'
}
nr=0 # We want the while loop to be able to manipulate these and
totalSize=0 #+ to be able to see the changes after the 'while' finished.
while read fileSize fileName ; do
echo "$fileName is $fileSize bytes"
let nr++
totalSize=$((totalSize+fileSize)) # Or: "let totalSize+=fileSize"
done&#60;&#60;EOF
$(doesOutput)
EOF
echo "$nr files totaling $totalSize bytes"</PRE
></FONT
></TD
></TR
></TABLE
>
</P
></DIV
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
SUMMARY="Footer navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="x17974.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="index.html"
ACCESSKEY="H"
>Home</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="redirapps.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>Using <I
CLASS="FIRSTTERM"
>exec</I
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="io-redirection.html"
ACCESSKEY="U"
>Up</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Applications</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>