internal.html

来自「BASH Shell 编程 经典教程 《高级SHELL脚本编程》中文版」· HTML 代码 · 共 3,324 行 · 第 1/5 页

HTML
3,324
字号
  3&nbsp;read var1 &#60;data-file  4&nbsp;echo "var1 = $var1"  5&nbsp;# var1将会把"data-file"的第一行的全部内容都为它的值.  6&nbsp;  7&nbsp;read var2 var3 &#60;data-file  8&nbsp;echo "var2 = $var2   var3 = $var3"  9&nbsp;# 注意, 这里的"read"命令将会产生一种不直观的行为.  10&nbsp;# 1) 重新从文件的开头开始读入变量. 11&nbsp;# 2) 每个变量都设置成了以空白分割的字符串. 12&nbsp;#    而不是之前的以整行的内容作为变量的值. 13&nbsp;# 3) 而最后一个变量将会取得第一行剩余的全部部分(译者注: 不管是否以空白分割). 14&nbsp;# 4) 如果需要赋值的变量个数比文件中第一行以空白分割的字符串个数还多的话,  15&nbsp;#    那么这些变量将会被赋空值. 16&nbsp; 17&nbsp;echo "------------------------------------------------" 18&nbsp; 19&nbsp;# 如何用循环来解决上边所提到的问题: 20&nbsp;while read line 21&nbsp;do 22&nbsp;  echo "$line" 23&nbsp;done &#60;data-file 24&nbsp;# 感谢, Heiner Steven 指出了这点. 25&nbsp; 26&nbsp;echo "------------------------------------------------" 27&nbsp; 28&nbsp;# 使用$IFS(内部域分隔变量)来将每行的输入单独的放到"read"中, 29&nbsp;# 前提是如果你不想使用默认空白的话. 30&nbsp; 31&nbsp;echo "List of all users:" 32&nbsp;OIFS=$IFS; IFS=:       # /etc/passwd 使用 ":" 作为域分隔符. 33&nbsp;while read name passwd uid gid fullname ignore 34&nbsp;do 35&nbsp;  echo "$name ($fullname)" 36&nbsp;done &#60;/etc/passwd   # I/O 重定向. 37&nbsp;IFS=$OIFS              # 恢复原始的$IFS. 38&nbsp;# 这段代码也是Heiner Steven编写的. 39&nbsp; 40&nbsp; 41&nbsp; 42&nbsp;#  在循环内部设置$IFS变量,  43&nbsp;#+ 而不用把原始的$IFS 44&nbsp;#+ 保存到临时变量中. 45&nbsp;#  感谢, Dim Segebart, 指出了这点. 46&nbsp;echo "------------------------------------------------" 47&nbsp;echo "List of all users:" 48&nbsp; 49&nbsp;while IFS=: read name passwd uid gid fullname ignore 50&nbsp;do 51&nbsp;  echo "$name ($fullname)" 52&nbsp;done &#60;/etc/passwd   # I/O 重定向. 53&nbsp; 54&nbsp;echo 55&nbsp;echo "\$IFS still $IFS" 56&nbsp; 57&nbsp;exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><DIVCLASS="NOTE"><P></P><TABLECLASS="NOTE"WIDTH="90%"BORDER="0"><TR><TDWIDTH="25"ALIGN="CENTER"VALIGN="TOP"><IMGSRC="./images/note.gif"HSPACE="5"ALT="Note"></TD><TDALIGN="LEFT"VALIGN="TOP"><P><AHREF="special-chars.html#PIPEREF">管道</A>输出到<BCLASS="COMMAND">read</B>命令中, 				使用管道<AHREF="internal.html#ECHOREF">echo</A>输出来设置变量<AHREF="gotchas.html#BADREAD0">将会失败</A>.</P><P><ANAME="READPIPEREF"></A>然而, 使用管道<AHREF="basic.html#CATREF">cat</A>输出<EM>看起来</EM>能够正常运行. </P><P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;cat file1 file2 |  2&nbsp;while read line  3&nbsp;do  4&nbsp;echo $line  5&nbsp;done</PRE></FONT></TD></TR></TABLE></P><P>但是, 就像Bj鰊 Eriksson所指出的:</P><DIVCLASS="EXAMPLE"><HR><ANAME="READPIPE"></A><P><B>例子 11-8. 管道输出到read中的问题</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;#!/bin/sh  2&nbsp;# readpipe.sh  3&nbsp;# 这个例子是由Bjon Eriksson所编写的.  4&nbsp;  5&nbsp;last="(null)"  6&nbsp;cat $0 |  7&nbsp;while read line  8&nbsp;do  9&nbsp;    echo "{$line}" 10&nbsp;    last=$line 11&nbsp;done 12&nbsp;printf "\nAll done, last:$last\n" 13&nbsp; 14&nbsp;exit 0  # 代码结束. 15&nbsp;        # 下边是脚本的(部分)输出. 16&nbsp;        # 'echo'出了多余的大括号. 17&nbsp; 18&nbsp;############################################# 19&nbsp; 20&nbsp;./readpipe.sh  21&nbsp; 22&nbsp;{#!/bin/sh} 23&nbsp;{last="(null)"} 24&nbsp;{cat $0 |} 25&nbsp;{while read line} 26&nbsp;{do} 27&nbsp;{echo "{$line}"} 28&nbsp;{last=$line} 29&nbsp;{done} 30&nbsp;{printf "nAll done, last:$lastn"} 31&nbsp; 32&nbsp; 33&nbsp;All done, last:(null) 34&nbsp; 35&nbsp;变量(last)被设置在子shell中, 并没有被设置在外边. </PRE></FONT></TD></TR></TABLE><HR></DIV><P>在许多Linux发行版上, <BCLASS="COMMAND">gendiff</B>脚本通常都在<TTCLASS="FILENAME">/usr/bin</TT>下, 		将<AHREF="moreadv.html#FINDREF">find</A>的输出通过管道传到<EM>while read</EM>结构中.	      <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;find $1 \( -name "*$2" -o -name ".*$2" \) -print |  2&nbsp;while read f; do  3&nbsp;. . .</PRE></FONT></TD></TR></TABLE>            </P></TD></TR></TABLE></DIV></DD></DL></DIV><P></P><DIVCLASS="VARIABLELIST"><P><B><ANAME="INTFILESYSTEM1"></A>文件系统</B></P><DL><DT><BCLASS="COMMAND">cd</B></DT><DD><P><BCLASS="COMMAND">cd</B>, 修改目录命令, 			  在脚本中用的最多的时候就是当命令需要在指定目录下运行时, 	      需要用它来修改当前工作目录. </P><P>  	      <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;(cd /source/directory &#38;&#38; tar cf - . ) | (cd /dest/directory &#38;&#38; tar xpvf -)</PRE></FONT></TD></TR></TABLE>	      [来自于<AHREF="special-chars.html#COXEX">之前引用过</A>的一个例子, 是由Alan Cox编写的]</P><P><CODECLASS="OPTION">-P</CODE> (physical)选项对于<BCLASS="COMMAND">cd</B>命令的意义是忽略符号链接. </P><P><BCLASS="COMMAND">cd -</B> 将会把工作目录修改至<AHREF="internalvariables.html#OLDPWD">$OLDPWD</A>, 也就是之前的工作目录. </P><P><ANAME="DOUBLESLASHREF"></A></P><DIVCLASS="CAUTION"><P></P><TABLECLASS="CAUTION"WIDTH="90%"BORDER="0"><TR><TDWIDTH="25"ALIGN="CENTER"VALIGN="TOP"><IMGSRC="./images/caution.gif"HSPACE="5"ALT="Caution"></TD><TDALIGN="LEFT"VALIGN="TOP"><P>当我们使用两个"/"来作为<BCLASS="COMMAND">cd</B>命令的参数时, 					结果却出乎我们的意料.	      .	      <TABLEBORDER="1"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="SCREEN"><SAMPCLASS="PROMPT">bash$ </SAMP><KBDCLASS="USERINPUT">cd //</KBD><SAMPCLASS="PROMPT">bash$ </SAMP><KBDCLASS="USERINPUT">pwd</KBD><SAMPCLASS="COMPUTEROUTPUT">//</SAMP>	      </PRE></FONT></TD></TR></TABLE>	      输出应该是, 并且当然应该是 <SAMPCLASS="COMPUTEROUTPUT">/</SAMP>.	      无论在命令下还是在脚本中, 这都是个问题. </P></TD></TR></TABLE></DIV></DD><DT><ANAME="PWD2REF"></A><BCLASS="COMMAND">pwd</B></DT><DD><P>打印出当前的工作目录. 			  这将给出用户(或脚本)的当前工作目录 (参考<AHREF="internal.html#EX37">例子 11-9</A>). 				  使用这个命令的结果和从内建变量<AHREF="internalvariables.html#PWDREF">$PWD</A>中所读取的值是相同的.</P></DD><DT><ANAME="DIRSD"></A><BCLASS="COMMAND">pushd</B>, <BCLASS="COMMAND">popd</B>, <BCLASS="COMMAND">dirs</B></DT><DD><P>这几个命令可以使得工作目录书签化, 就是可以按顺序向前或向后移动工作目录.	      压栈的动作可以保存工作目录列表. 选项可以允许对目录栈做不同的操作. </P><P><ANAME="PUSHDREF"></A><KBDCLASS="USERINPUT">pushd	      dir-name</KBD>把路径<TTCLASS="REPLACEABLE"><I>dir-name</I></TT>压入目录栈, 	      同时修改当前目录到<TTCLASS="REPLACEABLE"><I>dir-name</I></TT>. </P><P><ANAME="POPDREF"></A><BCLASS="COMMAND">popd</B>将目录栈最上边的目录弹出, 	      同时将当前目录修改为刚弹出来的那个目录. </P><P><BCLASS="COMMAND">dirs</B>列出所有目录栈的内容		  (与<AHREF="internalvariables.html#DIRSTACKREF">$DIRSTACK</A>变量相比较).	      一个成功的<BCLASS="COMMAND">pushd</B>或者<BCLASS="COMMAND">popd</B>将会自动调用<BCLASS="COMMAND">dirs</B>命令.</P><P>对于那些并没有对当前目录做硬编码, 并且需要对当前工作目录做灵活修改的脚本来说, 	      使用这些命令是再好不过了. 	      注意内建<CODECLASS="VARNAME">$DIRSTACK</CODE>数组变量,	      这个变量可以在脚本中进行访问, 并且它们保存了目录栈的内容.	    </P><DIVCLASS="EXAMPLE"><HR><ANAME="EX37"></A><P><B>例子 11-9. 修改当前工作目录</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;#!/bin/bash  2&nbsp;  3&nbsp;dir1=/usr/local  4&nbsp;dir2=/var/spool  5&nbsp;  6&nbsp;pushd $dir1  7&nbsp;# 将自动运行一个 'dirs' (把目录栈的内容列到stdout上).  8&nbsp;echo "Now in directory `pwd`." # 使用后置引用的 'pwd'.  9&nbsp; 10&nbsp;# 现在对'dir1'做一些操作. 11&nbsp;pushd $dir2 12&nbsp;echo "Now in directory `pwd`." 13&nbsp; 14&nbsp;# 现在对'dir2'做一些操作. 15&nbsp;echo "The top entry in the DIRSTACK array is $DIRSTACK." 16&nbsp;popd 17&nbsp;echo "Now back in directory `pwd`." 18&nbsp; 19&nbsp;# 现在, 对'dir1'做更多的操作. 20&nbsp;popd 21&nbsp;echo "Now back in original working directory `pwd`." 22&nbsp; 23&nbsp;exit 0 24&nbsp; 25&nbsp;# 如果你不使用 'popd' 将会发生什么 -- 然后退出这个脚本? 26&nbsp;# 你最后将落在哪个目录中? 为什么?</PRE></FONT></TD></TR></TABLE><HR></DIV></DD></DL></DIV><P></P><DIVCLASS="VARIABLELIST"><P><B><ANAME="INTVAR1"></A>变量</B></P><DL><DT><ANAME="LETREF"></A><BCLASS="COMMAND">let</B></DT><DD><P><BCLASS="COMMAND">let</B>命令将执行变量的算术操作. 			  在许多情况下, 它被看作是复杂的<AHREF="moreadv.html#EXPRREF">expr</A>命令的一个简化版本.</P><DIVCLASS="EXAMPLE"><HR><ANAME="EX46"></A><P><B>例子 11-10. 使用<SPANCLASS="QUOTE">"let"</SPAN>命令来做算术运算.</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;#!/bin/bash  2&nbsp;  3&nbsp;echo  4&nbsp;  5&nbsp;let a=11            # 与 'a=11' 相同  6&nbsp;let a=a+5           # 等价于 let "a = a + 5"  7&nbsp;                    # (双引号和空格是这句话更具可读性.)  8&nbsp;echo "11 + 5 = $a"  # 16  9&nbsp; 10&nbsp;let "a &#60;&#60;= 3"       # 等价于 let "a = a &#60;&#60; 3" 11&nbsp;echo "\"\$a\" (=16) left-shifted 3 places = $a" 12&nbsp;                    # 128 13&nbsp; 14&nbsp;let "a /= 4"        # 等价于 let "a = a / 4" 15&nbsp;echo "128 / 4 = $a" # 32 16&nbsp; 17&nbsp;let "a -= 5"        # 等价于 let "a = a - 5" 18&nbsp;echo "32 - 5 = $a"  # 27 19&nbsp; 20&nbsp;let "a *=  10"      # 等价于 let "a = a * 10" 21&nbsp;echo "27 * 10 = $a" # 270 22&nbsp; 23&nbsp;let "a %= 8"        # 等价于 let "a = a % 8" 24&nbsp;echo "270 modulo 8 = $a  (270 / 8 = 33, remainder $a)" 25&nbsp;                    # 6 26&nbsp; 27&nbsp;echo 28&nbsp; 29&nbsp;exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV></DD><DT><ANAME="EVALREF"></A><BCLASS="COMMAND">eval</B></DT><DD><P><KBDCLASS="USERINPUT">eval arg1 [arg2] ... [argN]</KBD></P><P>将表达式中的参数, 或者表达式列表, 组合起来, 			然后<EM>评价</EM>它们(译者注: 通常用来执行). 			任何被包含在表达示中的变量都将被扩展.	      结果将会被转化到命令中. 如果你想从命令行中或者是从脚本中产生代码, 那么这个命令就非常有用了. </P><P>	      <TABLEBORDER="1"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="SCREEN"><SAMPCLASS="PROMPT">bash$ </SAMP><KBDCLASS="USERINPUT">process=xterm</KBD><SAMPCLASS="PROMPT">bash$ </SAMP><KBDCLASS="USERINPUT">show_process="eval ps ax | grep $process"</KBD><SAMPCLASS="PROMPT">bash$ </SAMP><KBDCLASS="USERINPUT">$show_process</KBD><SAMPCLASS="COMPUTEROUTPUT">1867 tty1     S      0:02 xterm 2779 tty1     S      0:00 xterm 2886 pts/1    S      0:00 grep xterm</SAMP>	      </PRE></FONT></TD

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?