一个here document就是一段带有特殊目的的代码段. 它使用I/O重定向的形式将一个命令序列传递到一个交互程序或者命令中, 比如ftp, cat, 或者ex文本编辑器.
1 COMMAND < |
limit string用来界定命令序列的范围(译者注: 两个相同的limit string之间就是命令序列). 特殊符号<<用来标识limit string. 这个符号的作用就是将文件的输出重定向到程序或命令的stdin中. 与interactive-program < command-file很相似, 其中command-file包含:
1 command #1 2 command #2 3 ... |
而here document看上去是下面这个样子:
1 #!/bin/bash
2 interactive-program < |
选择一个名字非常诡异limit string能够有效的避免命令列表与limit string重名的问题.
注意, 某些情况下, 把here document用在非交互工具或命令中, 也会取得非常好的效果, 比如, wall.
例子 17-1. 广播: 将消息发送给每个登陆的用户
1 #!/bin/bash
2
3 wall < |
对于某些看上去不太可能的工具, 比如vi, 也能够使用here document.
例子 17-2. 虚拟文件: 创建一个2行的虚拟文件
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 < |
上边的脚本也可以不用vi而改用ex来实现, here document包含ex命令列表的形式足以形成自己的类别了, 称为ex script.
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 < |
与"ex script"相似的是cat script.
例子 17-3. 使用cat的多行消息
1 #!/bin/bash
2
3 # 'echo'对于打印单行消息来说是非常好用的,
4 #+ 但是在打印消息块时可能就有点问题了.
5 # 'cat' here document可以解决这个限制.
6
7 cat < $Newfile < |
-
选项用来标记here document的limit string (<<-LimitString), 可以抑制输出时前边的tab(不是空格). 这么做可以增加一个脚本的可读性.
例子 17-4. 带有抑制tab功能的多行消息
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 |
here document支持参数和命令替换. 所以也可以给here document的消息体传递不同的参数, 这样相应的也会修改输出.
例子 17-5. 使用参数替换的here document
1 #!/bin/bash
2 # 一个使用'cat'命令的here document, 使用了参数替换.
3
4 # 不传命令行参数给它, ./scriptname
5 # 传一个命令行参数给它, ./scriptname Mortimer
6 # 传一个包含2个单词(用引号括起来)的命令行参数给它,
7 # ./scriptname "Mortimer Jones"
8
9 CMDLINEparem=1 # 所期望的最少的命令行参数个数.
10
11 if [ $# -ge $CMDLINEparem ]
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 < |
这是一个非常有用的脚本, 其中使用了包含参数替换的here document.
例子 17-6. 上传一个文件对到"Sunsite"的incoming目录
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 < |
在here document的开头, 引用或转义"limit string", 会使得here document消息体中的参数替换被禁用.
例子 17-7. 关闭参数替换
1 #!/bin/bash 2 # 一个使用'cat'的here document, 但是禁用了参数替换. 3 4 NAME="John Doe" 5 RESPONDENT="the author of this fine script" 6 7 cat <<'Endofmessage' 8 9 Hello, there, $NAME. 10 Greetings to you, $NAME, from $RESPONDENT. 11 12 Endofmessage 13 14 # 如果"limit string"被引用或转义的话, 那么就禁用了参数替换. 15 # 下边的两种方式具有相同的效果. 16 # cat <<"Endofmessage" 17 # cat <<\Endofmessage 18 19 exit 0 |
禁用了参数替换后, 将允许输出文本本身(译者注: 就是未转义的原文). 如果你想产生脚本甚至是程序代码的话, 那么可以使用这种办法.
例子 17-8. 生成另外一个脚本的脚本
1 #!/bin/bash 2 # generate-script.sh 3 # 这个脚本的诞生基于Albert Reiner的一个主意. 4 5 OUTFILE=generated.sh # 所产生文件的名字. 6 7 8 # ----------------------------------------------------------- 9 # 'Here document包含了需要产生的脚本的代码. 10 ( 11 cat <<'EOF' 12 #!/bin/bash 13 14 echo "This is a generated shell script." 15 # Note that since we are inside a subshell, 16 #+ we can't access variables in the "outside" script. 17 18 echo "Generated file will be named: $OUTFILE" 19 # Above line will not work as normally expected 20 #+ because paremeter expansion has been disabled. 21 # Instead, the result is literal output. 22 23 a=7 24 b=3 25 26 let "c = $a * $b" 27 echo "c = $c" 28 29 exit 0 30 EOF 31 ) > $OUTFILE 32 # ----------------------------------------------------------- 33 34 # 将'limit string'引用起来将会阻止上边 35 #+ here document消息体中的变量扩展. 36 # 这会使得输出文件中的内容保持here document消息体中的原文. 37 38 if [ -f "$OUTFILE" ] 39 then 40 chmod 755 $OUTFILE 41 # 让所产生的文件具有可执行权限. 42 else 43 echo "Problem in creating file: \"$OUTFILE\"" 44 fi 45 46 # 这个方法也可以用来产生 47 #+ C程序代码, Perl程序代码, Python程序代码, makefile, 48 #+ 和其他的一些类似的代码. 49 # (译者注: 中间一段没译的注释将会被here document打印出来) 50 exit 0 |
也可以将here document的输出保存到变量中.
1 variable=$(cat < |
A here document can supply input to a function in the same script.
例子 17-9. Here document与函数
1 #!/bin/bash
2 # here-function.sh
3
4 GetPersonalData ()
5 {
6 read firstname
7 read lastname
8 read address
9 read city
10 read state
11 read zipcode
12 } # 这个函数看起来就是一个交互函数, 但是...
13
14
15 # 给上边的函数提供输入.
16 GetPersonalData < |
也可以这么使用:(冒号), 做一个假命令来从一个here document中接收输出. 这么做事实上就是创建了一个"匿名"的here document.
例子 17-10. "匿名"的here Document
1 #!/bin/bash
2
3 : < |
上边所示技术的一种变化, 可以用来"注释"掉代码块. |
例子 17-11. 注释掉一段代码块
1 #!/bin/bash
2 # commentblock.sh
3
4 : < |
关于这种小技巧的另一个应用就是能够产生"自文档化(self-documenting)"的脚本. |
例子 17-12. 一个自文档化(self-documenting)的脚本
1 #!/bin/bash
2 # self-document.sh: 自文档化(self-documenting)的脚本
3 # 修改于"colm.sh".
4
5 DOC_REQUEST=70
6
7 if [ "$1" = "-h" -o "$1" = "--help" ] # 请求帮助.
8 then
9 echo; echo "Usage: $0 [directory-name]"; echo
10 sed --silent -e '/DOCUMENTATIONXX$/,/^DOCUMENTATIONXX$/p' "$0" |
11 sed -e '/DOCUMENTATIONXX$/d'; exit $DOC_REQUEST; fi
12
13
14 : < |
使用cat脚本也能够完成相同的目的.
1 DOC_REQUEST=70
2
3 if [ "$1" = "-h" -o "$1" = "--help" ] # 请求帮助.
4 then # 使用"cat脚本" . . .
5 cat < |
请参考例子 A-28可以看到更多关于"自文档化"脚本的好例子.
Here document创建临时文件, 但是这些文件将在打开后被删除, 并且不能够被任何其他进程所访问.
|
某些工具是不能放入here document中运行的. |
结尾的limit string, 就是here document最后一行的limit string, 必须从第一个字符开始. 它的前面不能够有任何前置的空白. 而在这个limit string后边的空白也会引起异常. 空白将会阻止limit string的识别. (译者注: 下边这个脚本由于结束limit string的问题, 造成脚本无法结束, 所有内容全部被打印出来, 所以注释就不译了, 保持这个例子脚本的原样.)
|
对于那些使用"here document", 并且非常复杂的任务, 最好考虑使用expect脚本语言, 这种语言就是为了达到向交互程序添加输入的目的而量身定做的.