📄 ch45_22.htm
字号:
<HTML><!--Distributed by F --><HEAD><TITLE>[Chapter 45] 45.22 Handling Files Line-by-Line </TITLE><METANAME="DC.title"CONTENT="UNIX Power Tools"><METANAME="DC.creator"CONTENT="Jerry Peek, Tim O'Reilly & Mike Loukides"><METANAME="DC.publisher"CONTENT="O'Reilly & Associates, Inc."><METANAME="DC.date"CONTENT="1998-08-04T21:54:49Z"><METANAME="DC.type"CONTENT="Text.Monograph"><METANAME="DC.format"CONTENT="text/html"SCHEME="MIME"><METANAME="DC.source"CONTENT="1-56592-260-3"SCHEME="ISBN"><METANAME="DC.language"CONTENT="en-US"><METANAME="generator"CONTENT="Jade 1.1/O'Reilly DocBook 3.0 to HTML 4.0"><LINKREV="made"HREF="mailto:online-books@oreilly.com"TITLE="Online Books Comments"><LINKREL="up"HREF="ch45_01.htm"TITLE="45. Shell Programming for the Initiated"><LINKREL="prev"HREF="ch45_21.htm"TITLE="45.21 n>&m: Swap Standard Output and Standard Error "><LINKREL="next"HREF="ch45_23.htm"TITLE="45.23 The Ins and Outs of Redirected I/O Loops "></HEAD><BODYBGCOLOR="#FFFFFF"TEXT="#000000"><DIVCLASS="htmlnav"><H1><IMGSRC="gifs/smbanner.gif"ALT="UNIX Power Tools"USEMAP="#srchmap"BORDER="0"></H1><MAPNAME="srchmap"><AREASHAPE="RECT"COORDS="0,0,466,58"HREF="index.htm"ALT="UNIX Power Tools"><AREASHAPE="RECT"COORDS="467,0,514,18"HREF="jobjects/fsearch.htm"ALT="Search this book"></MAP><TABLEWIDTH="515"BORDER="0"CELLSPACING="0"CELLPADDING="0"><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="172"><ACLASS="SECT1"HREF="ch45_21.htm"TITLE="45.21 n>&m: Swap Standard Output and Standard Error "><IMGSRC="gifs/txtpreva.gif"SRC="gifs/txtpreva.gif"ALT="Previous: 45.21 n>&m: Swap Standard Output and Standard Error "BORDER="0"></A></TD><TDALIGN="CENTER"VALIGN="TOP"WIDTH="171"><B><FONTFACE="ARIEL,HELVETICA,HELV,SANSERIF"SIZE="-1">Chapter 45<BR>Shell Programming for the Initiated</FONT></B></TD><TDALIGN="RIGHT"VALIGN="TOP"WIDTH="172"><ACLASS="SECT1"HREF="ch45_23.htm"TITLE="45.23 The Ins and Outs of Redirected I/O Loops "><IMGSRC="gifs/txtnexta.gif"SRC="gifs/txtnexta.gif"ALT="Next: 45.23 The Ins and Outs of Redirected I/O Loops "BORDER="0"></A></TD></TR></TABLE> <HRALIGN="LEFT"WIDTH="515"TITLE="footer"></DIV><DIVCLASS="SECT1"><H2CLASS="sect1"><ACLASS="title"NAME="UPT-ART-7923">45.22 Handling Files Line-by-Line </A></H2><PCLASS="para"><ACLASS="indexterm"NAME="UPT-ART-7923-IX-FILES-READING-LINE-BY-LINE"></A><ACLASS="indexterm"NAME="UPT-ART-7923-IX-SHELL-SCRIPTS-READING-FILES-LINE-BY-LINE"></A><ACLASS="indexterm"NAME="AUTOID-53840"></A>It isn't easy to see how to read a file line-by-line in a shell script.And while youcan write a file line-by-line by using the file-appending operator<CODECLASS="literal">>></CODE> (two right angle brackets) with each command that should addto the file, there's a more efficient way to do that as well.</P><PCLASS="para"><ACLASS="indexterm"NAME="AUTOID-53845"></A><ACLASS="indexterm"NAME="AUTOID-53847"></A><ACLASS="indexterm"NAME="AUTOID-53850"></A><ACLASS="indexterm"NAME="AUTOID-53853"></A><ACLASS="indexterm"NAME="AUTOID-53856"></A>The trick is to open the file and associate a file descriptornumber (3, 4, ..., 9) with it.UNIX keeps a <EMCLASS="emphasis">file pointer</EM>, like a bookmark in a book, that tells itwhere the next read or write should be in each open file.For example, if you open a file for reading and read the first line, the filepointer will stay at the start of the second line.The next read from that same open file will move the pointer to thestart of the third line.This trick only works with files that stay open; each time you open a file,the file pointer is set to the start of the file.[1]The Bourne shell<SPANCLASS="link"><EMCLASS="emphasis">exec</EM> command (<ACLASS="linkend"HREF="ch45_07.htm"TITLE="The exec Command ">45.7</A>)</SPAN>can open a file and associate a file descriptor with it.For example, this <EMCLASS="emphasis">exec</EM> command makes the standard input of allfollowing commands come from the file <EMCLASS="emphasis">formfile</EM>:</P><BLOCKQUOTECLASS="footnote"><PCLASS="para">[1] The file-appending operator <CODECLASS="literal">>></CODE> sets the pointer to the end ofthe file before the first write.</P></BLOCKQUOTE><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen"><ICLASS="lineannotation">...all commands read their stdin from default place</I>exec < formfile <ICLASS="lineannotation">...all commands will read their stdin from formfile</I></PRE></BLOCKQUOTE></P><PCLASS="para">There's another way to rearrange file descriptors: by doing it at the lastline of <EMCLASS="emphasis">while</EM> loops, <EMCLASS="emphasis">if</EM> and <EMCLASS="emphasis">case</EM> statements.For example, all commands in the <EMCLASS="emphasis">while</EM> loop below will take theirstandard inputs from the file <EMCLASS="emphasis">formfile</EM>.The standard input outside the <EMCLASS="emphasis">while</EM> loop isn't changed:</P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen"><ICLASS="lineannotation">...all commands read their stdin from default place</I>while ...do <ICLASS="lineannotation">...all commands will read their stdin from formfile</I>done < formfile <ICLASS="lineannotation">...all commands read their stdin from default place</I></PRE></BLOCKQUOTE></P><PCLASS="para">I call those "redirected-I/O loops."Those and other Bourne shell structures<SPANCLASS="link">have some problems (<ACLASS="linkend"HREF="ch45_23.htm"TITLE="The Ins and Outs of Redirected I/O Loops ">45.23</A>)</SPAN>,but they're usually worth the work to solve.</P><PCLASS="para"><ACLASS="indexterm"NAME="UPT-ART-7923-IX-FORMPROG-SCRIPT-EXAMPLES"></A><ACLASS="indexterm"NAME="UPT-ART-7923-IX-FORMPROG-SCRIPT-DESCRIBED"></A>We'll use all that to make a shell script for filling in forms.The script, <EMCLASS="emphasis">formprog</EM>, reads an empty form file like this one, lineby line:</P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen"><ACLASS="indexterm"NAME="UPT-ART-7923-IX-FORMS-SCRIPT-FOR-FILING"></A>Name:Address:City:State/Province:Phone:FAX: Project: Corporate DecisionComments:</PRE></BLOCKQUOTE></P><PCLASS="para">If a line has just a label, like <CODECLASS="literal">Name:</CODE>, the script will promptyou to fill it in.If you do, the script will add the completed line to an output file; otherwise,no output line is written.If a form line is already completed, like:</P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen">Project: Corporate Decision</PRE></BLOCKQUOTE></P><PCLASS="para">the script doesn't prompt you; it just writes the lineto the output file:</P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen">% <CODECLASS="userinput"><B>formprog formfile completed</B></CODE>Name: <CODECLASS="userinput"><B>Jerry Peek</B></CODE>Address: <CODECLASS="userinput"><B>123 Craigie St.</B></CODE>City: <CODECLASS="userinput"><B>Cambridge</B></CODE>State/Province: <CODECLASS="userinput"><B>MA</B></CODE>Phone: <CODECLASS="userinput"><B>(617)456-7890</B></CODE>FAX: Project: Corporate DecisionComments: % <CODECLASS="userinput"><B>cat completed</B></CODE>Name: Jerry PeekAddress: 123 Craigie St.City: CambridgeState/Province: MAPhone: (617)456-7890Project: Corporate Decision</PRE></BLOCKQUOTE></P><PCLASS="para">Here's the <EMCLASS="emphasis">formprog</EM> script.The line numbers are for reference only; don't type them into the file.There's more explanation after the script:</P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen"><ACLASS="indexterm"NAME="AUTOID-53915"></A><ACLASS="indexterm"NAME="AUTOID-53918"></A> 1 #!/bin/sh 2 # formprog - fill in template form from $1, leave completed form in $2 3 # TABSTOPS ARE SET AT 4 IN THIS SCRIPT 4 5 template="$1" completed="$2" errors=/tmp/formprog$$ 6 myname=`basename $0` # BASENAME OF THIS SCRIPT (NO LEADING PATH) 7 trap 'rm -f $errors; exit' 0 1 2 15 8 <ACLASS="indexterm"NAME="AUTOID-53921"></A> 9 # READ $template LINE-BY-LINE, WRITE COMPLETED LINES TO $completed:10 exec 4<&0 # SAVE ORIGINAL stdin (USUALLY TTY) AS FD 411 while read label text12 do<ACLASS="indexterm"NAME="AUTOID-53924"></A>13 case "$label" in14 ?*:) # FIRST WORD ENDS WITH A COLON; LINE IS OKAY15 case "$text" in16 ?*) # SHOW LINE ON SCREEN AND PUT INTO completed FILE:17 echo "$label $text"18 echo "$label $text" 1>&319 ;;20 *) # FILL IT IN OURSELVES:21 echo -n "$label "22 exec 5<&0 # SAVE template FILE FD; DO NOT CLOSE!23 exec 0<&4 # RESTORE ORIGINAL stdin TO READ ans24 read ans25 exec 0<&5 # RECONNECT template FILE TO stdin26 case "$ans" in27 "") ;; # EMPTY; DO NOTHING28 *) echo "$label $ans" 1>&3 ;;29 esac30 ;;31 esac32 ;;33 *) echo "$myname: bad $1 line: '$label $text'" 1>&2; break;;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -