📄 ch47_02.htm
字号:
<HTML><!--Distributed by F --><HEAD><TITLE>[Chapter 47] 47.2 C Shell Programming Considered Harmful </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:55:53Z"><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="ch47_01.htm"TITLE="47. C Shell Programming...NOT"><LINKREL="prev"HREF="ch47_01.htm"TITLE="47.1 Why Not? "><LINKREL="next"HREF="ch47_03.htm"TITLE="47.3 Conditional Statements with if "></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="ch47_01.htm"TITLE="47.1 Why Not? "><IMGSRC="gifs/txtpreva.gif"SRC="gifs/txtpreva.gif"ALT="Previous: 47.1 Why Not? "BORDER="0"></A></TD><TDALIGN="CENTER"VALIGN="TOP"WIDTH="171"><B><FONTFACE="ARIEL,HELVETICA,HELV,SANSERIF"SIZE="-1">Chapter 47<BR>C Shell Programming...NOT</FONT></B></TD><TDALIGN="RIGHT"VALIGN="TOP"WIDTH="172"><ACLASS="SECT1"HREF="ch47_03.htm"TITLE="47.3 Conditional Statements with if "><IMGSRC="gifs/txtnexta.gif"SRC="gifs/txtnexta.gif"ALT="Next: 47.3 Conditional Statements with if "BORDER="0"></A></TD></TR></TABLE> <HRALIGN="LEFT"WIDTH="515"TITLE="footer"></DIV><DIVCLASS="SECT1"><H2CLASS="sect1"><ACLASS="title"NAME="UPT-ART-0393">47.2 C Shell Programming Considered Harmful </A></H2><PCLASS="para"><ACLASS="indexterm"NAME="UPT-ART-393-IX-BOURNE-SHELL-SH-C-SHELL-COMPARED-TO"></A><ACLASS="indexterm"NAME="UPT-ART-393-IX-C-SHELL-CSH-BOURNE-SHELL-COMPARED-TO"></A><ACLASS="indexterm"NAME="UPT-ART-393-IX-C-SHELL-CSH-BUGS-IN"></A><ACLASS="indexterm"NAME="UPT-ART-393-IX-C-SHELL-CSH-DISADVANTAGES"></A>Resolved: the <EMCLASS="emphasis">csh</EM> is a tool utterly inadequate for programming, andits use for such purposes should be strictly banned.</P><PCLASS="para">I am continually shocked and dismayed to see people write test cases,install scripts, and other random hackery using the <EMCLASS="emphasis">csh</EM>.</P><PCLASS="para">The <EMCLASS="emphasis">csh</EM> is seductive because the conditionals are more C-like,so the path of least resistance is chosen and a <EMCLASS="emphasis">csh</EM> script iswritten. Sadly, this is a lost cause, and the programmer seldom evenrealizes it, even when he finds that many simple things he wishes todo range from cumbersome to impossible in the <EMCLASS="emphasis">csh</EM>.</P><PCLASS="para">What's more, lack of proficiency in the Bourne shell has been known tocause errors in <EMCLASS="emphasis">/etc/rc</EM> and <EMCLASS="emphasis">.cronrc</EM> files, which is aproblem, because you <EMCLASS="emphasis">must</EM> write these files in that language.</P><DIVCLASS="sect2"><H3CLASS="sect2"><ACLASS="title"NAME="UPT-ART-393-SECT-1.1">47.2.1 File Descriptors </A></H3><PCLASS="para"><ACLASS="indexterm"NAME="AUTOID-56081"></A><ACLASS="indexterm"NAME="AUTOID-56084"></A>The most common problem encountered in <EMCLASS="emphasis">csh</EM> programming is thatyou can't do file-descriptor manipulation. All you are able to do isredirect <EMCLASS="emphasis">stdin</EM>, or <EMCLASS="emphasis">stdout</EM>, or <EMCLASS="emphasis">dup</EM> <EMCLASS="emphasis">stderr</EM> into<EMCLASS="emphasis">stdout</EM>. Bourne-compatible shells offer you an abundance of moreexotic possibilities.</P><DIVCLASS="sect3"><H4CLASS="sect3"><ACLASS="title"NAME="UPT-ART-393-SECT-1.1.1">47.2.1.1 Writing Files </A></H4><PCLASS="para"><ACLASS="indexterm"NAME="AUTOID-56096"></A><ACLASS="indexterm"NAME="AUTOID-56099"></A><ACLASS="indexterm"NAME="AUTOID-56101"></A><ACLASS="indexterm"NAME="AUTOID-56104"></A>In the Bourne shell, you can open or <EMCLASS="emphasis">dup</EM> random file descriptors.For example, </P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen">exec 2>errs.out</PRE></BLOCKQUOTE></P><PCLASS="para">means that from then on, <EMCLASS="emphasis">stderr</EM> goes into the <EMCLASS="emphasis">errs.out</EM> file.</P><PCLASS="para">Or what if you just want to throw away <EMCLASS="emphasis">stderr</EM> and leave <EMCLASS="emphasis">stdout</EM>alone?Pretty simple operation, eh?</P><PCLASS="para"><TABLECLASS="screen.co"BORDER="1"><TR><THVALIGN="TOP"><PRECLASS="calloutlist"><ACLASS="co"HREF="ch13_14.htm"TITLE="13.14 What Can You Do with an Empty File? ">/dev/null</A> </PRE></TH><TDVALIGN="TOP"><PRECLASS="screen">cmd 2>/dev/null<ACLASS="indexterm"NAME="AUTOID-56119"></A></PRE></TD></TR></TABLE></P><PCLASS="para">That works in the Bourne shell.In the C shell, you can only make a pitiful attempt like this:</P><PCLASS="para"><TABLECLASS="screen.co"BORDER="1"><TR><THVALIGN="TOP"><PRECLASS="calloutlist"><ACLASS="co"HREF="ch45_20.htm"TITLE="45.20 Overview: Open Files and File Descriptors ">/dev/tty</A> </PRE></TH><TDVALIGN="TOP"><PRECLASS="screen">(cmd > /dev/tty) >& /dev/null</PRE></TD></TR></TABLE></P><PCLASS="para">But who said that <EMCLASS="emphasis">stdout</EM> was my terminal?So it's wrong.This simple operation <EMCLASS="emphasis">cannot be done</EM> in the C shell.</P><PCLASS="para"><ACLASS="indexterm"NAME="AUTOID-56131"></A><ACLASS="indexterm"NAME="AUTOID-56134"></A>Along these same lines, you can't direct error messages in <EMCLASS="emphasis">csh</EM> scripts on <EMCLASS="emphasis">stderr</EM>, as is considered proper.In the Bourne shell, youmight say:</P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen">echo "$0: cannot find $file" 1>&2</PRE></BLOCKQUOTE></P><PCLASS="para">but in the C shell, you can't redirect <EMCLASS="emphasis">stdout</EM> onto <EMCLASS="emphasis">stderr</EM>so you endup doing something silly like this:</P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen">sh -c "echo '${0}: cannot find $file' 1>&2"</PRE></BLOCKQUOTE></P></DIV><DIVCLASS="sect3"><H4CLASS="sect3"><ACLASS="title"NAME="UPT-ART-393-SECT-1.1.2">47.2.1.2 Reading Files </A></H4><PCLASS="para"><ACLASS="indexterm"NAME="AUTOID-56149"></A><ACLASS="indexterm"NAME="AUTOID-56152"></A><ACLASS="indexterm"NAME="AUTOID-56155"></A><ACLASS="indexterm"NAME="AUTOID-56158"></A><ACLASS="indexterm"NAME="AUTOID-56161"></A><ACLASS="indexterm"NAME="AUTOID-56164"></A><ACLASS="indexterm"NAME="AUTOID-56167"></A><ACLASS="indexterm"NAME="AUTOID-56170"></A>In the <EMCLASS="emphasis">csh</EM>, all you've got is <CODECLASS="literal">$<</CODE>,which reads a line from your <EMCLASS="emphasis">tty</EM>.What if you've redirected <EMCLASS="emphasis">stdin</EM>?Tough noogies, you still get your <EMCLASS="emphasis">tty</EM>, which you really can't redirect.Now, the <EMCLASS="emphasis">read</EM> statement in the Bourne shell allows you to read from<EMCLASS="emphasis">stdin</EM>, which catches redirection.It also means that you can do things like this:</P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen">exec 3< file1exec 4< file2</PRE></BLOCKQUOTE></P><PCLASS="para"> Now you can read from file descriptor 3 and get lines from <EMCLASS="emphasis">file1</EM>, or from<EMCLASS="emphasis">file2</EM> through fd 4.In modern, Bourne-like shells, this suffices: </P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen">read some_var 0<&3read another_var 0<&4</PRE></BLOCKQUOTE></P><PCLASS="para">Although in older ones where <EMCLASS="emphasis">read</EM> only goes from 0, you trick it:</P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen">exec 5<&0 # save old stdinexec 0<&3; read some_varexec 0<&4; read another_varexec 0<&5 # restore it</PRE></BLOCKQUOTE></P></DIV><DIVCLASS="sect3"><H4CLASS="sect3"><ACLASS="title"NAME="UPT-ART-393-SECT-1.1.3">47.2.1.3 Closing FDs </A></H4><PCLASS="para"><ACLASS="indexterm"NAME="AUTOID-56194"></A><ACLASS="indexterm"NAME="AUTOID-56196"></A><ACLASS="indexterm"NAME="AUTOID-56199"></A>In the Bourne shell, you can close file descriptors you don'twant open, like <CODECLASS="literal">2>&-</CODE>, which isn't the same as redirecting itto <EMCLASS="emphasis">/dev/null</EM>. </P></DIV><DIVCLASS="sect3"><H4CLASS="sect3"><ACLASS="title"NAME="UPT-ART-393-SECT-1.1.4">47.2.1.4 More Elaborate Combinations </A></H4><PCLASS="para"><ACLASS="indexterm"NAME="AUTOID-56207"></A><ACLASS="indexterm"NAME="AUTOID-56210"></A>Maybe you want to pipe <EMCLASS="emphasis">stderr</EM> to a command and leave <EMCLASS="emphasis">stdout</EM> alone.Not too hard an idea, right?As I mentioned above, you can't do this in the C shell.In a Bourne shell, you can do things like this:</P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen">$ <CODECLASS="userinput"><B>exec 3>&1; grep yyy xxx 2>&1 1>&3 3>&- | sed s/file/foobar/ 1>&2 3>&-</B></CODE>grep: xxx: No such foobar or directory</PRE></BLOCKQUOTE></P><PCLASS="para"><ACLASS="indexterm"NAME="AUTOID-56219"></A><ACLASS="indexterm"NAME="AUTOID-56222"></A><ACLASS="indexterm"NAME="AUTOID-56225"></A><ACLASS="indexterm"NAME="AUTOID-56228"></A><ACLASS="indexterm"NAME="AUTOID-56231"></A><ACLASS="indexterm"NAME="AUTOID-56234"></A>Normal output would be unaffected.The fd closes (<CODECLASS="literal">3>&-</CODE>) were there in casesomething really cared about all its FDs.We send <EMCLASS="emphasis">stderr</EM> to <EMCLASS="emphasis">sed</EM>,and then put it back out FD 2.</P><PCLASS="para">Consider the pipeline:</P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen"><CODECLASS="replaceable"><I>A</I></CODE> | <CODECLASS="replaceable"><I>B</I></CODE> | <CODECLASS="replaceable"><I>C</I></CODE></PRE></BLOCKQUOTE></P><PCLASS="para"><ACLASS="indexterm"NAME="AUTOID-56247"></A>You want to know the status of <EMCLASS="emphasis">C</EM>, well, that's easy: it's in <CODECLASS="literal">$?</CODE>,or <CODECLASS="literal">$status</CODE> in <EMCLASS="emphasis">csh</EM>.But if you want it from <EMCLASS="emphasis">A</EM>, you're out of luck - ifyou're in the C shell, that is.In the Bourne shell, you can get it, although doing so is a bit tricky. Here's something I had to do where I ran <EMCLASS="emphasis">dd</EM>'s<EMCLASS="emphasis">stderr</EM> into a <EMCLASS="emphasis">grep -v</EM> pipe to get rid of the <CODECLASS="literal">recordsin/out</CODE> noise, but hadto return the <EMCLASS="emphasis">dd</EM>'s exit status, not the <EMCLASS="emphasis">grep</EM>'s:</P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen">device=/dev/rmt8dd_noise='^[0-9]+\+[0-9]+ records (in|out)$'<ACLASS="indexterm"NAME="AUTOID-56263"></A>exec 3>&1status=`((dd if=$device ibs=64k 2>&1 1>&3 3>&- 4>&-; echo $? >&4) | egrep -v "$dd_noise" 1>&2 3>&- 4>&-) 4>&1`exit $status;</PRE></BLOCKQUOTE></P></DIV></DIV><DIVCLASS="sect2"><H3CLASS="sect2"><ACLASS="title"NAME="UPT-ART-393-SECT-1.2">47.2.2 Command Orthogonality </A></H3><DIVCLASS="sect3"><H4CLASS="sect3"><ACLASS="title"NAME="UPT-ART-393-SECT-1.2.1">47.2.2.1 Built-Ins </A></H4><PCLASS="para"><ACLASS="indexterm"NAME="AUTOID-56271"></A>The <EMCLASS="emphasis">csh</EM> is a horrid botch with its built-ins.You can't put them together in any reasonable way.Even a simple little thing like this: </P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen"><ACLASS="indexterm"NAME="AUTOID-56277"></A><ACLASS="indexterm"NAME="AUTOID-56280"></A>% <CODECLASS="userinput"><B>time | echo</B></CODE></PRE></BLOCKQUOTE></P><PCLASS="para"> while nonsensical, shouldn't give me this message:</P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen">Reset tty pgrp from 9341 to 26678</PRE></BLOCKQUOTE></P><PCLASS="para">Others are more fun:</P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen"><ACLASS="indexterm"NAME="AUTOID-56290"></A><ACLASS="indexterm"NAME="AUTOID-56293"></A>% <CODECLASS="userinput"><B>sleep 1 | while</B></CODE>while: Too few arguments.[5] 9402% <CODECLASS="userinput"><B>jobs</B></CODE>[5] 9402 Done sleep |</PRE></BLOCKQUOTE></P><PCLASS="para"><ACLASS="indexterm"NAME="AUTOID-56299"></A><ACLASS="indexterm"NAME="AUTOID-56302"></A><ACLASS="indexterm"NAME="AUTOID-56305"></A><ACLASS="indexterm"NAME="AUTOID-56308"></A>Some can even hang your shell.Try typing CTRL-z while you're <EMCLASS="emphasis">source</EM>ing something, or redirecting a <EMCLASS="emphasis">source</EM> command.Just make sure you have another window handy. </P></DIV><DIVCLASS="sect3"><H4CLASS="sect3"><ACLASS="title"NAME="UPT-ART-393-SECT-1.2.2">47.2.2.2 Flow Control </A></H4><PCLASS="para"><ACLASS="indexterm"NAME="AUTOID-56316"></A><ACLASS="indexterm"NAME="AUTOID-56319"></A>You can't mix flow control and commands, like this:</P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen">who | while read line; do echo "gotta $line"done</PRE></BLOCKQUOTE></P><PCLASS="para">You can't combine multiline pipes constructs in a <EMCLASS="emphasis">csh</EM> using semicolons.There's no easy way to do this:</P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen">alias cmd 'if (foo) then bar; else snark; endif'</PRE></BLOCKQUOTE></P></DIV><DIVCLASS="sect3"><H4CLASS="sect3"><ACLASS="title"NAME="UPT-ART-393-SECT-1.2.3">47.2.2.3 Stupid Parsing Bugs </A></H4><PCLASS="para"><ACLASS="indexterm"NAME="AUTOID-56331"></A><ACLASS="indexterm"NAME="AUTOID-56334"></A><ACLASS="indexterm"NAME="AUTOID-56337"></A><ACLASS="indexterm"NAME="AUTOID-56340"></A><ACLASS="indexterm"NAME="AUTOID-56343"></A>Certain reasonable things just don't work, like this:</P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen">% <CODECLASS="userinput"><B>kill -1 `cat foo`</B></CODE>`cat foo`: Ambiguous.</PRE></BLOCKQUOTE></P><PCLASS="para">But this is ok:</P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen">% <CODECLASS="userinput"><B>/bin/kill -1 `cat foo`</B></CODE></PRE></BLOCKQUOTE></P><PCLASS="para">If you have a stopped job:</P><PCLASS="para"><BLOCKQUOTECLASS="screen"><PRECLASS="screen">[2] Stopped rlogin globhost</PRE></BLOCKQUOTE></P><PCLASS="para">You should be able to kill it with: </P
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -