📄 here-docs.html
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><HTML><HEAD><TITLE>Here Document</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="重定向的应用"HREF="redirapps.html"><LINKREL="NEXT"TITLE="Here String"HREF="x13628.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="redirapps.html"ACCESSKEY="P">前一页</A></TD><TDWIDTH="80%"ALIGN="center"VALIGN="bottom"></TD><TDWIDTH="10%"ALIGN="right"VALIGN="bottom"><AHREF="x13628.html"ACCESSKEY="N">下一页</A></TD></TR></TABLE><HRALIGN="LEFT"WIDTH="100%"></DIV><DIVCLASS="CHAPTER"><H1><ANAME="HERE-DOCS"></A>17. Here Document</H1><TABLEBORDER="0"WIDTH="100%"CELLSPACING="0"CELLPADDING="0"CLASS="EPIGRAPH"><TR><TDWIDTH="45%"> </TD><TDWIDTH="45%"ALIGN="LEFT"VALIGN="TOP"><I><P><I>Here and now, boys.</I></P></I></TD></TR><TR><TDWIDTH="45%"> </TD><TDWIDTH="45%"ALIGN="RIGHT"VALIGN="TOP"><I><SPANCLASS="ATTRIBUTION">Aldous Huxley, <SPANCLASS="QUOTE">"Island"</SPAN></SPAN></I></TD></TR></TABLE><P><ANAME="HEREDOCREF"></A></P><P>一个<ICLASS="FIRSTTERM">here document</I>就是一段带有特殊目的的代码段. 它使用<AHREF="io-redirection.html#IOREDIRREF">I/O重定向</A>的形式将一个命令序列传递到一个交互程序或者命令中, 比如<AHREF="communications.html#FTPREF">ftp</A>, <AHREF="basic.html#CATREF">cat</A>, 或者<ICLASS="FIRSTTERM">ex</I>文本编辑器. </P><P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 COMMAND <<InputComesFromHERE 2 ... 3 InputComesFromHERE</PRE></FONT></TD></TR></TABLE></P><P><ICLASS="FIRSTTERM">limit string</I>用来界定命令序列的范围(译者注: 两个相同的limit string之间就是命令序列). 特殊符号<SPANCLASS="TOKEN"><<</SPAN>用来标识limit string. 这个符号的作用就是将文件的输出重定向到程序或命令的<TTCLASS="FILENAME">stdin</TT>中. 与<KBDCLASS="USERINPUT">interactive-program < command-file</KBD>很相似, 其中<TTCLASS="FILENAME">command-file</TT>包含: <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 command #1 2 command #2 3 ...</PRE></FONT></TD></TR></TABLE></P><P>而<EM>here document</EM>看上去是下面这个样子: </P><P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 interactive-program <<LimitString 3 command #1 4 command #2 5 ... 6 LimitString</PRE></FONT></TD></TR></TABLE></P><P>选择一个名字非常诡异<EM>limit string</EM>能够有效的避免命令列表与<EM>limit string</EM>重名的问题. </P><P>注意, 某些情况下, 把<EM>here document</EM>用在非交互工具或命令中, 也会取得非常好的效果, 比如, <AHREF="system.html#WALLREF">wall</A>. </P><DIVCLASS="EXAMPLE"><HR><ANAME="EX70"></A><P><B>例子 17-1. <BCLASS="COMMAND">广播</B>: 将消息发送给每个登陆的用户</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 3 wall <<zzz23EndOfMessagezzz23 4 E-mail your noontime orders for pizza to the system administrator. 5 (Add an extra dollar for anchovy or mushroom topping.) 6 # 附加的消息文本放在这里. 7 # 注意: 'wall'命令会把注释行也打印出来. 8 zzz23EndOfMessagezzz23 9 10 # 当然, 更有效率的做法是: 11 # wall <message-file 12 # 然而, 将消息模版嵌入到脚本中 13 #+ 只是一种"小吃店"(译者注: 方便但是不卫生)的做法, 而且这种做法是一次性的. 14 15 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><P>对于某些看上去不太可能的工具, 比如<ICLASS="FIRSTTERM">vi</I>, 也能够使用<EM>here document</EM>. </P><DIVCLASS="EXAMPLE"><HR><ANAME="EX69"></A><P><B>例子 17-2. <BCLASS="COMMAND">虚拟文件</B>: 创建一个2行的虚拟文件</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 3 # 用非交互的方式来使用'vi'编辑一个文件. 4 # 模仿'sed'. 5 6 E_BADARGS=65 7 8 if [ -z "$1" ] 9 then 10 echo "Usage: `basename $0` filename" 11 exit $E_BADARGS 12 fi 13 14 TARGETFILE=$1 15 16 # 在文件中插入两行, 然后保存. 17 #--------Begin here document-----------# 18 vi $TARGETFILE <<x23LimitStringx23 19 i 20 This is line 1 of the example file. 21 This is line 2 of the example file. 22 ^[ 23 ZZ 24 x23LimitStringx23 25 #----------End here document-----------# 26 27 # 注意上边^[是一个转义符, 键入Ctrl+v <Esc>就行, 28 #+ 事实上它是<Esc>键;. 29 30 # Bram Moolenaar指出这种方法不能使用在'vim'上, (译者注: Bram Moolenaar是vim作者) 31 #+ 因为可能会存在终端相互影响的问题. 32 33 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><P> 上边的脚本也可以不用<BCLASS="COMMAND">vi</B>而改用<BCLASS="COMMAND">ex</B>来实现, <EM>here document</EM>包含<BCLASS="COMMAND">ex</B>命令列表的形式足以形成自己的类别了, 称为<ICLASS="FIRSTTERM">ex script</I>. <TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # 把所有后缀为".txt"文件 3 #+ 中的"Smith"都替换成"Jones". 4 5 ORIGINAL=Smith 6 REPLACEMENT=Jones 7 8 for word in $(fgrep -l $ORIGINAL *.txt) 9 do 10 # ------------------------------------- 11 ex $word <<EOF 12 :%s/$ORIGINAL/$REPLACEMENT/g 13 :wq 14 EOF 15 # :%s是"ex"的替换命令. (译者注: 与vi和vim的基本命令相同) 16 # :wq是保存并退出的意思. 17 # ------------------------------------- 18 done</PRE></FONT></TD></TR></TABLE> </P><P><ANAME="CATSCRIPTREF"></A></P><P>与<SPANCLASS="QUOTE">"ex script"</SPAN>相似的是<ICLASS="FIRSTTERM">cat script</I>. </P><DIVCLASS="EXAMPLE"><HR><ANAME="EX71"></A><P><B>例子 17-3. 使用<BCLASS="COMMAND">cat</B>的多行消息</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 3 # 'echo'对于打印单行消息来说是非常好用的, 4 #+ 但是在打印消息块时可能就有点问题了. 5 # 'cat' here document可以解决这个限制. 6 7 cat <<End-of-message 8 ------------------------------------- 9 This is line 1 of the message. 10 This is line 2 of the message. 11 This is line 3 of the message. 12 This is line 4 of the message. 13 This is the last line of the message. 14 ------------------------------------- 15 End-of-message 16 17 # 用下边这行代替上边的第7行, 18 #+ cat > $Newfile <<End-of-message 19 #+ ^^^^^^^^^^ 20 #+ 那么就会把输出写到文件$Newfile中, 而不是stdout. 21 22 exit 0 23 24 25 #-------------------------------------------- 26 # 下边的代码不会运行, 因为上边有"exit 0". 27 28 # S.C. 指出下边代码也能够达到相同目的. 29 echo "------------------------------------- 30 This is line 1 of the message. 31 This is line 2 of the message. 32 This is line 3 of the message. 33 This is line 4 of the message. 34 This is the last line of the message. 35 -------------------------------------" 36 # 然而, 文本中可能不允许包含双引号, 除非它们被转义. </PRE></FONT></TD></TR></TABLE><HR></DIV><P><CODECLASS="OPTION">-</CODE>选项用来标记here document的limit string (<KBDCLASS="USERINPUT"><<-LimitString</KBD>), 可以抑制输出时前边的tab(不是空格). 这么做可以增加一个脚本的可读性. </P><DIVCLASS="EXAMPLE"><HR><ANAME="EX71A"></A><P><B>例子 17-4. 带有抑制tab功能的多行消息</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # 与之前的例子相同, 但是... 3 4 # - 选项对于here docutment来说, 5 #+ <<-可以抑制文档体前边的tab, 6 #+ 而*不*是空格. 7 8 cat <<-ENDOFMESSAGE 9 This is line 1 of the message. 10 This is line 2 of the message. 11 This is line 3 of the message. 12 This is line 4 of the message. 13 This is the last line of the message. 14 ENDOFMESSAGE 15 # 脚本在输出的时候左边将被刷掉. 16 # 就是说每行前边的tab将不会显示. 17 18 # 上边5行"消息"的前边都是tab, 而不是空格. 19 # 空格是不受<<-影响的. 20 21 # 注意, 这个选项对于*嵌在*中间的tab没作用. 22 23 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><P><EM>here document</EM>支持参数和命令替换. 所以也可以给here document的消息体传递不同的参数, 这样相应的也会修改输出. </P><DIVCLASS="EXAMPLE"><HR><ANAME="EX71B"></A><P><B>例子 17-5. 使用参数替换的here document</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # 一个使用'cat'命令的here document, 使用了参数替换. 3 4 # 不传命令行参数给它, ./scriptname 5 # 传一个命令行参数给它, ./scriptname Mortimer 6 # 传一个包含2个单词(用引号括起来)的命令行参数给它, 7 # ./scriptname "Mortimer Jones" 8 9 CMDLINEPARAM=1 # 所期望的最少的命令行参数个数. 10 11 if [ $# -ge $CMDLINEPARAM ] 12 then 13 NAME=$1 # 如果命令行参数超过1个, 14 #+ 那么就只取第一个参数. 15 else 16 NAME="John Doe" # 默认情况下, 如果没有命令行参数的话. 17 fi 18 19 RESPONDENT="the author of this fine script" 20 21 22 cat <<Endofmessage 23 24 Hello, there, $NAME. 25 Greetings to you, $NAME, from $RESPONDENT. 26 27 # This comment shows up in the output (why?). 28 29 Endofmessage 30 31 # 注意上边的空行也打印输出, 32 # 而上边那行"注释"当然也会打印到输出. 33 # (译者注: 这就是为什么不翻译那行注释的原因, 尽量保持代码的原样) 34 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><P>这是一个非常有用的脚本, 其中使用了包含参数替换的here document. </P><DIVCLASS="EXAMPLE"><HR><ANAME="EX72"></A><P><B>例子 17-6. 上传一个文件对到<SPANCLASS="QUOTE">"Sunsite"</SPAN>的incoming目录</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # upload.sh 3 4 # 上传这一对文件(Filename.lsm, Filename.tar.gz) 5 #+ 到Sunsite/UNC (ibiblio.org)的incoming目录. 6 # Filename.tar.gz是自身的tar包. 7 # Filename.lsm是描述文件. 8 # Sunsite需要"lsm"文件, 否则就拒绝上传. 9 10 11 E_ARGERROR=65 12 13 if [ -z "$1" ] 14 then 15 echo "Usage: `basename $0` Filename-to-upload" 16 exit $E_ARGERROR 17 fi 18 19 20 Filename=`basename $1` # 从文件名中去掉目录字符串. 21 22 Server="ibiblio.org" 23 Directory="/incoming/Linux" 24 # 在这里也不一定非得将上边的参数写死在这个脚本中, 25 #+ 可以使用命令行参数的方法来替换. 26 27 Password="your.e-mail.address" # 可以修改成相匹配的密码. 28 29 ftp -n $Server <<End-Of-Session 30 # -n选项禁用自动登录. 31 32 user anonymous "$Password" 33 binary 34 bell # 在每个文件传输后, 响铃. 35 cd $Directory 36 put "$Filename.lsm" 37 put "$Filename.tar.gz" 38 bye 39 End-Of-Session 40 41 exit 0</PRE></FONT></TD></TR></TABLE><HR></DIV><P>在here document的开头, 引用或转义<SPANCLASS="QUOTE">"limit string"</SPAN>, 会使得here document消息体中的参数替换被禁用. </P><DIVCLASS="EXAMPLE"><HR><ANAME="EX71C"></A><P><B>例子 17-7. 关闭参数替换</B></P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><FONTCOLOR="#000000"><PRECLASS="PROGRAMLISTING"> 1 #!/bin/bash 2 # 一个使用'cat'的here document, 但是禁用了参数替换. 3 4 NAME="John Doe"
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -