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 #!/bin/bash 2 # spawn.sh 3 4 5 PIDS=$(pidof sh $0) # 这个脚本不同实例的进程ID. 6 P_array=( $PIDS ) # 把它们放到数组里(为什么?). 7 echo $PIDS # 显示父进程和子进程的进程ID. 8 let "instances = ${#P_array[*]} - 1" # 计算元素个数, 至少为1. 9 # 为什么减1? 10 echo "$instances instance(s) of this script running." 11 echo "[Hit Ctl-C to exit.]"; echo 12 13 14 sleep 1 # 等一下. 15 sh $0 # 再来一次, Sam. 16 17 exit 0 # 没必要; 脚本永远不会运行到这里. 18 # 为什么运行不到这里? 19 20 # 在使用Ctl-C退出之后, 21 #+ 是否所有产生出来的进程都会被kill掉? 22 # 如果是这样的话, 为什么? 23 24 # 注意: 25 # ---- 26 # 小心, 不要让这个脚本运行太长时间. 27 # 它最后会吃掉你绝大多数的系统资源. 28 29 # 是否有合适的脚本技术, 30 #+ 用于产生脚本自身的大量实例. 31 # 为什么或为什么不?</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 #!/bin/bash 2 3 echo "This line uses the \"echo\" builtin." 4 /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 echo Hello 2 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 if echo "$VAR" | grep -q txt # if [[ $VAR = *txt* ]] 2 then 3 echo "$VAR contains the substring sequence \"txt\"" 4 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 # 嵌入一个换行? 2 echo "Why doesn't this string \n split on two lines?" 3 # 上边这句的\n将被打印出来. 达不到换行的目的. 4 5 # 让我们再试试其他方法. 6 7 echo 8 9 echo $"A line of text containing 10 a linefeed." 11 # 打印出两个独立的行(嵌入换行成功了). 12 # 但是, 是否必须有"$"作为变量前缀? 13 14 echo 15 16 echo "This string splits 17 on two lines." 18 # 不, 并不是非有"$"不可. 19 20 echo 21 echo "---------------" 22 echo 23 24 echo -n $"Another line of text containing 25 a linefeed." 26 # 打印出两个独立的行(嵌入换行成功了). 27 # 即使使用了-n选项, 也没能阻止换行. (译者注: -n 阻止了第2个换行) 28 29 echo 30 echo 31 echo "---------------" 32 echo 33 echo 34 35 # 然而, 下边的代码就没能像期望的那样运行. 36 # 为什么失败? 提示: 因为分配到了变量. 37 string1=$"Yet another line of text containing 38 a linefeed (maybe)." 39 40 echo $string1 41 # Yet another line of text containing a linefeed (maybe). 42 # ^ 43 # 换行变成了空格. 44 45 # 感谢, 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 + -
显示快捷键?