internal.html

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

HTML
3,324
字号
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><HTML><HEAD><TITLE>内部命令与内建命令</TITLE><METANAME="GENERATOR"CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINKREL="HOME"TITLE="高级Bash脚本编程指南"HREF="index.html"><LINKREL="UP"TITLE="进阶"HREF="part3.html"><LINKREL="PREVIOUS"TITLE="测试与分支(case与select结构)"HREF="testbranch.html"><LINKREL="NEXT"TITLE="作业控制命令"HREF="x6756.html"></HEAD><BODYCLASS="CHAPTER"BGCOLOR="#FFFFFF"TEXT="#000000"LINK="#0000FF"VLINK="#840084"ALINK="#0000FF"><DIVCLASS="NAVHEADER"><TABLESUMMARY="Header navigation table"WIDTH="100%"BORDER="0"CELLPADDING="0"CELLSPACING="0"><TR><THCOLSPAN="3"ALIGN="center">高级Bash脚本编程指南: 一本深入学习shell脚本艺术的书籍</TH></TR><TR><TDWIDTH="10%"ALIGN="left"VALIGN="bottom"><AHREF="testbranch.html"ACCESSKEY="P">前一页</A></TD><TDWIDTH="80%"ALIGN="center"VALIGN="bottom"></TD><TDWIDTH="10%"ALIGN="right"VALIGN="bottom"><AHREF="x6756.html"ACCESSKEY="N">下一页</A></TD></TR></TABLE><HRALIGN="LEFT"WIDTH="100%"></DIV><DIVCLASS="CHAPTER"><H1><ANAME="INTERNAL"></A>11. 内部命令与内建命令</H1><P><ANAME="BUILTINREF"></A><ICLASS="FIRSTTERM">内建命令</I>指的就是包含在Bash工具包中的<BCLASS="COMMAND">命令</B>, 			  从字面意思上看就是<EM>built in</EM>. 			  这主要是考虑到执行效率的问题 -- 内建命令将比外部命令执行的更快, 			  一部分原因是因为外部命令通常都需要fork出一个单独的进程来执行 --			  另一部分原因是特定的内建命令需要直接访问shell的内核部分.		</P><P><ANAME="FORKREF"></A></P><TABLECLASS="SIDEBAR"BORDER="1"CELLPADDING="5"><TR><TD><DIVCLASS="SIDEBAR"><P></P><ANAME="AEN5884"></A><P>当一个命令或者是shell本身需要初始化(或者<ICLASS="FIRSTTERM">创建</I>)一个新的子进程来执行一个任务的时候, 			 这种行为被称为<ICLASS="FIRSTTERM">fork</I>. 			 这个新产生的进程被叫做<ICLASS="FIRSTTERM">子进程</I>, 			 并且这个进程是从<ICLASS="FIRSTTERM">父进程</I>中<EM>fork</EM>出来的. 			 当<ICLASS="FIRSTTERM">子进程</I>执行它的任务时, 		   <ICLASS="FIRSTTERM">父进程</I>也在运行.</P><P>注意: 当<EM>父进程</EM>获得了<EM>子进程</EM>的<ICLASS="FIRSTTERM">进程ID</I>时, 		   父进程可以给子进程传递参数,		   然而<EM>反过来却不行</EM>. 		   <AHREF="gotchas.html#PARCHILDPROBREF">这将会产生不可思议的并且很难追踪的问题. </A>	   </P><DIVCLASS="EXAMPLE"><HR><ANAME="SPAWNSCR"></A><P><B>例子 11-1. 一个fork出多个自身实例的脚本</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;#!/bin/bash  2&nbsp;# spawn.sh  3&nbsp;  4&nbsp;  5&nbsp;PIDS=$(pidof sh $0)  # 这个脚本不同实例的进程ID.   6&nbsp;P_array=( $PIDS )    # 把它们放到数组里(为什么?).  7&nbsp;echo $PIDS           # 显示父进程和子进程的进程ID.   8&nbsp;let "instances = ${#P_array[*]} - 1"  # 计算元素个数, 至少为1.  9&nbsp;                                      # 为什么减1? 10&nbsp;echo "$instances instance(s) of this script running." 11&nbsp;echo "[Hit Ctl-C to exit.]"; echo 12&nbsp; 13&nbsp; 14&nbsp;sleep 1              # 等一下. 15&nbsp;sh $0                # 再来一次, Sam. 16&nbsp; 17&nbsp;exit 0               # 没必要; 脚本永远不会运行到这里. 18&nbsp;                     # 为什么运行不到这里? 19&nbsp; 20&nbsp;#  在使用Ctl-C退出之后, 21&nbsp;#+ 是否所有产生出来的进程都会被kill掉? 22&nbsp;#  如果是这样的话, 为什么? 23&nbsp; 24&nbsp;# 注意: 25&nbsp;# ---- 26&nbsp;# 小心, 不要让这个脚本运行太长时间. 27&nbsp;# 它最后会吃掉你绝大多数的系统资源. 28&nbsp; 29&nbsp;#  是否有合适的脚本技术,  30&nbsp;#+ 用于产生脚本自身的大量实例. 31&nbsp;#  为什么或为什么不?</PRE></FONT></TD></TR></TABLE><HR></DIV><P>通常情况下, 脚本中的Bash<EM>内建命令</EM>在运行的时候是不会fork出一个子进程的. 		   但是脚本中的外部或者过滤命令通常<EM>会</EM>fork出一个子进程. </P><P></P></DIV></TD></TR></TABLE><P>一个内建命令通常会与一个系统命令同名, 			   但是Bash在内部重新实现了这些命令. 			   比如, Bash的<BCLASS="COMMAND">echo</B>命令与<TTCLASS="FILENAME">/bin/echo</TT>就不尽相同, 			   虽然它们的行为在绝大多数情况下都是一样的.	   <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;#!/bin/bash  2&nbsp;  3&nbsp;echo "This line uses the \"echo\" builtin."  4&nbsp;/bin/echo "This line uses the /bin/echo system command."</PRE></FONT></TD></TR></TABLE>	</P><P><ANAME="KEYWORDREF"></A><ICLASS="FIRSTTERM">关键字</I>的意思就是<EM>保留</EM>字, 			  对于shell来说关键字具有特殊的含义, 并且用来构建shell语法结构.	比如, <SPANCLASS="QUOTE">"<SPANCLASS="TOKEN">for</SPAN>"</SPAN>,	<SPANCLASS="QUOTE">"<SPANCLASS="TOKEN">while</SPAN>"</SPAN>, <SPANCLASS="QUOTE">"do"</SPAN>, 和	<SPANCLASS="QUOTE">"<SPANCLASS="TOKEN">!</SPAN>"</SPAN> 都是关键字. 	与<AHREF="internal.html#BUILTINREF">内建命令</A>相似的是, 关键字也是Bash的骨干部分, 	但是与<EM>内建命令</EM>不同的是, 关键字本身并不是一个命令, 	而是一个比较大的命令结构的一部分.	   <ANAME="AEN5922"HREF="#FTN.AEN5922"><SPANCLASS="footnote">[1]</SPAN></A>	</P><P></P><DIVCLASS="VARIABLELIST"><P><B><ANAME="INTIO1"></A>I/O</B></P><DL><DT><ANAME="ECHOREF"></A><BCLASS="COMMAND">echo</B></DT><DD><P>打印(到 <TTCLASS="FILENAME">stdout</TT>)一个表达式或者变量(参考<AHREF="varsubn.html#EX9">例子 4-1</A>). 	      <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;echo Hello  2&nbsp;echo $a</PRE></FONT></TD></TR></TABLE></P><P><BCLASS="COMMAND">echo</B>命令需要<CODECLASS="OPTION">-e</CODE>参数来打印转义字符. 	      参考<AHREF="escapingsection.html#ESCAPED">例子 5-2</A>.</P><P>通常情况下, 每个<BCLASS="COMMAND">echo</B>命令都会在终端上新起一行,  	      但是<CODECLASS="OPTION">-n</CODE>参数会阻止新起一行. </P><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><BCLASS="COMMAND">echo</B>命令可以作为输入, 通过管道传递到一系列命令中去.	      </P><P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;if echo "$VAR" | grep -q txt   # if [[ $VAR = *txt* ]]  2&nbsp;then  3&nbsp;  echo "$VAR contains the substring sequence \"txt\""  4&nbsp;fi</PRE></FONT></TD></TR></TABLE></P></TD></TR></TABLE></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><BCLASS="COMMAND">echo</B>命令可以与<AHREF="commandsub.html#COMMANDSUBREF">命令替换</A>组合起来, 				这样可以用来设置一个变量.</P><P><KBDCLASS="USERINPUT">a=`echo	      "HELLO" | tr A-Z a-z`</KBD></P><P>参考<AHREF="textproc.html#LOWERCASE">例子 12-19</A>, <AHREF="moreadv.html#EX57">例子 12-3</A>, <AHREF="mathc.html#MONTHLYPMT">例子 12-42</A>, 和 <AHREF="mathc.html#BASE">例子 12-43</A>.</P></TD></TR></TABLE></DIV><P>小心<BCLASS="COMMAND">echo `command`</B>将会删除任何由<TTCLASS="REPLACEABLE"><I>command</I></TT>所产生的换行符. 	      </P><P><AHREF="internalvariables.html#IFSREF">$IFS</A> (内部域分隔符) 			  一搬都会将 <SPANCLASS="TOKEN">\n</SPAN> (换行符) 包含在它的<AHREF="special-chars.html#WHITESPACEREF">空白</A>字符集合中. 			  Bash因此会根据参数中的换行来分离<TTCLASS="REPLACEABLE"><I>command</I></TT>的输出, 			  然后<BCLASS="COMMAND">echo</B>.	      最后<BCLASS="COMMAND">echo</B>将以空格代替换行来输出这些参数. 	     </P><P>	      <TABLEBORDER="1"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="SCREEN"><SAMPCLASS="PROMPT">bash$ </SAMP><KBDCLASS="USERINPUT">ls -l /usr/share/apps/kjezz/sounds</KBD><SAMPCLASS="COMPUTEROUTPUT">-rw-r--r--    1 root     root         1407 Nov  7  2000 reflect.au -rw-r--r--    1 root     root          362 Nov  7  2000 seconds.au</SAMP><SAMPCLASS="PROMPT">bash$ </SAMP><KBDCLASS="USERINPUT">echo `ls -l /usr/share/apps/kjezz/sounds`</KBD><SAMPCLASS="COMPUTEROUTPUT">total 40 -rw-r--r-- 1 root root 716 Nov 7 2000 reflect.au -rw-r--r-- 1 root root 362 Nov 7 2000 seconds.au</SAMP>	      </PRE></FONT></TD></TR></TABLE>	    </P><P>				所以, 我们怎么做才能够在一个需要<EM>echo</EM>出来的字符串中嵌入换行呢?	     <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING">  1&nbsp;# 嵌入一个换行?  2&nbsp;echo "Why doesn't this string \n split on two lines?"  3&nbsp;# 上边这句的\n将被打印出来. 达不到换行的目的.  4&nbsp;  5&nbsp;# 让我们再试试其他方法.  6&nbsp;  7&nbsp;echo  8&nbsp;	       9&nbsp;echo $"A line of text containing 10&nbsp;a linefeed." 11&nbsp;# 打印出两个独立的行(嵌入换行成功了). 12&nbsp;# 但是, 是否必须有"$"作为变量前缀?  13&nbsp; 14&nbsp;echo 15&nbsp; 16&nbsp;echo "This string splits 17&nbsp;on two lines." 18&nbsp;# 不, 并不是非有"$"不可. 19&nbsp; 20&nbsp;echo 21&nbsp;echo "---------------" 22&nbsp;echo 23&nbsp; 24&nbsp;echo -n $"Another line of text containing 25&nbsp;a linefeed." 26&nbsp;# 打印出两个独立的行(嵌入换行成功了). 27&nbsp;# 即使使用了-n选项, 也没能阻止换行. (译者注: -n 阻止了第2个换行) 28&nbsp; 29&nbsp;echo 30&nbsp;echo 31&nbsp;echo "---------------" 32&nbsp;echo 33&nbsp;echo 34&nbsp; 35&nbsp;# 然而, 下边的代码就没能像期望的那样运行. 36&nbsp;# 为什么失败? 提示: 因为分配到了变量. 37&nbsp;string1=$"Yet another line of text containing 38&nbsp;a linefeed (maybe)." 39&nbsp; 40&nbsp;echo $string1 41&nbsp;# Yet another line of text containing a linefeed (maybe). 42&nbsp;#                                    ^ 43&nbsp;# 换行变成了空格. 44&nbsp; 45&nbsp;# 感谢, Steve Parker, 指出了这点.</PRE></FONT></TD></TR></TABLE>            </P><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>这个命令是shell的一个内建命令, 				与<TTCLASS="FILENAME">/bin/echo</TT>不同, 虽然行为相似.</P><P>	      <TABLEBORDER="1"BGCOLOR="#E0E0E0"WIDTH="90%"><TR><TD><FONTCOLOR="#000000"

⌨️ 快捷键说明

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