Chinaunix首页 | 论坛 | 博客
  • 博客访问: 531752
  • 博文数量: 134
  • 博客积分: 7990
  • 博客等级: 少将
  • 技术积分: 1290
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-29 11:43
文章分类

全部博文(134)

文章存档

2009年(7)

2008年(80)

2007年(47)

我的朋友

分类: LINUX

2008-01-05 21:46:39

here document 就是一段特殊目的的代码块. 他使用的形式来将一个命令序列传递到一个交互程序或者命令中, 比如, , 或者ex文本编辑器.

   1 COMMAND <

limit string 用来划定命令序列的范围(译者注: 两个相同的limit string之间就是命令序列). 特殊符号 << 用来表识limit string. 这个符号具有重定向文件的输出到程序或命令的输入的作用. 与 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 用在非交互工具和命令上的时候也会有好的效果, 比如, .


Example 17-1. 广播: 发送消息给每个登录上的用户

   1 #!/bin/bash
   2 
   3 wall <

即使是某些不大可能的工具, 如vi也可以使用here document.


Example 17-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 <就行,
  28 #+ 事实上它是键.
  29 
  30 #  Bram Moolenaar指出这种方法不能正常地用在'vim'上, (译者注: Bram Moolenaar是vim作者)
  31 #+ 因为可能会有终端的相互影响问题.
  32 
  33 exit 0

上边的脚本也可以不用vi而用ex来实现. Here document 包含ex命令列表的做法足够形成自己的类别了, 叫ex scripts.

   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 scripts"相似的是cat scripts.


Example 17-3. 使用cat的多行消息

   1 #!/bin/bash
   2 
   3 #  'echo' 对于打印单行消息是非常好的,
   4 #+  但是在打印消息块时可能就有点问题了.
   5 #   'cat' here document可以解决这个限制.
   6 
   7 cat < $Newfile <

- 选项用来标记here document的limit string (<<-LimitString), 可以抑制输出时前边的tab(不是空格). 这可以增加一个脚本的可读性.


Example 17-4. 带有抑制tab功能的多行消息

   1 #!/bin/bash
   2 # 与之前的例子相同, 但是...
   3 
   4 #  - 选项对于here docutment来说,<<-
   5 #+ 可以抑制文档体前边的tab,
   6 #+ 而*不*是空格 *not* spaces.
   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的消息体传递不同的参数, 这样相应的也会修改输出.


Example 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 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 <

这是一个包含参数替换的here document的有用的脚本.


Example 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的消息体中的参数替换被禁用.


Example 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

禁用了参数替换后, 将允许输出文本本身(译者注: 就是未转义的原文). 产生脚本甚至是程序代码就是这种用法的用途之一.


Example 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 parameter 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 <

同一脚本中的函数也可以接受here document的输出作为自身的参数.


Example 17-9. Here documents与函数

   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.


Example 17-10. "匿名" here Document

   1 #!/bin/bash
   2 
   3 : <

Tip

上边所示技术的一种变化可以用来"注释"掉代码块.


Example 17-11. 注释掉一段代码块

   1 #!/bin/bash
   2 # commentblock.sh
   3 
   4 : <

Tip

关于这种小技巧的另一个应用就是能够产生自文档化(self-documenting)的脚本.


Example 17-12. 一个自文档化(self-documenting)的脚本

   1 #!/bin/bash
   2 # self-document.sh: 自文档化(self-documenting)的脚本
   3 # Modification of "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 : <

使用 也能够完成相同的目的.

   1 DOC_REQUEST=70
   2 
   3 if [ "$1" = "-h"  -o "$1" = "--help" ]     # 请求帮助.
   4 then                                       # 使用"cat 脚本" . . .
   5   cat <

参见 可以了解更多关于自文档化脚本的好例子.

Note

Here document创建临时文件, 但是这些文件将在打开后被删除, 并且不能够被任何其他进程所存取.

 bash$ bash -c 'lsof -a -p $$ -d0' << EOF
 > EOF
 lsof    1213 bozo    0r   REG    3,5    0 30386 /tmp/t1213-0-sh (deleted)
        

Caution

某些工具是不能工作在here document中的.

Warning

结束的limit string, 就是here document最后一行的limit string, 必须开始于第一个字符位置. 它的前面不能够有任何前置的空白. 而在这个limit string后边的空白也会引起异常问题. 空白将会阻止limit string的识别.(译者注: 下边这个脚本由于结束limit string的问题, 造成脚本无法结束, 所有内容全部被打印出来, 所以注释就不译了, 保持例子脚本的原样.)

   1 #!/bin/bash
   2 
   3 echo "----------------------------------------------------------------------"
   4 
   5 cat <

对于那些使用"here document"得非常复杂的任务, 最好考虑使用expect脚本语言, 这种语言就是为了达到向交互程序添加输入的目的而量身定做的.

here string 可以被认为是here document的一种定制形式. 除了COMMAND <<<$WORD 就什么都没有了, $WORD将被扩展并且被送入COMMAND的stdin中.


Example 17-13. 在一个文件的开头添加文本

   1 #!/bin/bash
   2 # prepend.sh: 在文件的开头添加文本.
   3 #
   4 #  Kenny Stauffer所捐助的脚本例子,
   5 #+ 被本文作者作了少量的修改.
   6 
   7 
   8 E_NOSUCHFILE=65
   9 
  10 read -p "File: " file   # 'read'命令的 -p 参数显示提示符.
  11 if [ ! -e "$file" ]
  12 then   # 如果没有这个文件那就进来.
  13   echo "File $file not found."
  14   exit $E_NOSUCHFILE
  15 fi
  16 
  17 read -p "Title: " title
  18 cat - $file <<<$title > $file.new
  19 
  20 echo "Modified file is $file.new"
  21 
  22 exit 0
  23 
  24 # 下边是'man bash'中的一段:
  25 # Here Strings
  26 #  here document的一种变形,形式如下:
  27 # 
  28 #   <<

 

如果你觉得本文不错,可以订阅本站 | Google Reader | Bloglines | 抓虾 | 鲜果

阅读(1496) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~