高级shell编程笔记(第十七章 Here Documents)

第十七章 Here Documents

here document就是一段特殊的代码块。他使用I/O重定向的形式来将一个命令序列传递到一个交互程序或者命令中,比如ftp、cat或者ex文本编辑器。

limit string 用来划定命令序列的范围(注: 两个相同的 limit string 之间就是命令序列)。特殊符号 << 用来标识 limit string。这个符号具有重定向文件的输出到程序或命令的输入的作用。与 interactive-program < command-file 很相象。
command-file 包含:

command #1
command #2
...

而here document的形式看上去是如下样子:

#!/bin/bash
interactive-program << LimitString
command #1
command #2
...
LimitString

选择一个名字非常诡异的 limit string 将会避免命令列表和 limit string 重名的问题。注意:某些时候 here document 用在非交互工具和命令上的时候也会有好的效果,比如:wall。

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

#!/bin/bash
#
wall << zzz23EndOfMessagezzz23
E-mail your noontime orders for pizza to the system administrator.
#额外的消息文本写在这里.
# 注意: 'wall' 会打印注释行.
zzz23EndOfMessagezzz23

#可以使用更有效的做法:
#wall < message-file
#然而将消息模版嵌入到脚本中是一种"小吃店"(快速但是比较脏)的只能使用一次的解决办法.
exit 0

即使是某些不大可能的工具,如 vi 也可以使用 here document。(亲测貌似不适用,会产生一个交换文件)

Example 17-2 仿造文件:创建一个两行的仿造文件

#!/bin/bash
#
#用非交互的方式来使用vi编辑一个文件。:模仿sed
E_BADARGS=65
if [ -z "$1" ];then
  echo "Usage: `basename $0` filename"
  exit $E_BADARGS
fi
TARGETFILE=$1

#在文件中插入两行,然后保存。
vi $TARGETFILE << x23LimitStringx23
i
This is line 1 of the example file.
This is line 2 of the example file.
^[   #^[是一个转义符,键入 Ctrl+v 就行,事实上它是键.
ZZ
x23LimitStringx23
# Bram Moolenaar 指出这种方法不能正常地用在'vim'上,因为可能会有终端的相互影响问题.
exit 0

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

#!/bin/bash
#
#把所有后缀为".txt"文件中的"Smith"都替换成"Jones".
ORIGINAL=Smith
REPLACEMENT=Jones

for word in $(fgrep -l $ORIGINAL *.txt)   #grep -l:列出文件内容符合指定的样式的文件名称
do
  ex $word <<EOF
  :%s/$ORIGINAL/$REPLACEMENT/g
  :wq
EOF
done


与"ex scripts"相似的是 cat scripts。

Example 17-3 使用cat的多行消息
#!/bin/bash
#
#'echo' 对于打印单行消息是非常好的,但是在打印消息块时可能就有点问题了.
#'cat' here document 可以解决这个限制.

cat << End-of-message
-------------------------------------
This is line 1 of the message.
This is line 2 of the message.
This is line 3 of the message.
This is line 4 of the message.
This is line last of the message.
-------------------------------------
End-of-message

#用下边这行代替上边的第 7 行,会把输出写到文件$Newfile 中, 而不是 stdout.
#cat > $Newfile << End-of-message

exit 0

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

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

#!/bin/bash
#
#与之前的例子相同, 但是...
#- 选项对于 here docutment 来说,<<-可以抑制文档体前边的 tab,而不是空格.

cat <<- End-of-message
    This is line 1 of the message.
    This is line 2 of the message.
    This is line 3 of the message.
    This is line 4 of the message.
    This is line last of the message.
End-of-message
#脚本在输出的时候左边将被刷掉.
#就是说每行前边的 tab 将不会显示.
#上边 5 行"消息"的前边都是 tab, 不是空格.空格是不受<<-影响的.
# 注意, 这个选项对于*嵌在*中间的 tab 没作用.
exit 0

here document 支持参数和命令替换。所以也可以给 here document 的消息体传递不同的参数,这样相应的也会修改输出。

Example 17-5 使用参数替换的here document

#!/bin/bash
#

CMDLINEPAPAM=1
if [ $# -ge $CMDLINEPAPAM ];then
  NAME=$1
else
  NAME="John Doe"
fi

RESPONDENT="the author of this fine script"  

cat << EOF

Hello,there,$NAME
Greetings to you, $NAME, from $RESPONDENT.
#This comment shows up in the output (why?).
EOF
exit 0

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

Example 17-6 上传一个文件对到"Sunsite"的 incoming 目录

#!/bin/bash
#
E_ARGERROR=65

if [ -z "$1" ];then
  echo "Usage: `basename $0` Filename-to-upload"
  exit $E_ARGERROR
fi

Filename=`basename $1`   # 从文件名中去掉目录字符串.

Server="192.168.1.69"
Directory="/tmp"
Password="123456"

ftp -n $Server <<EOF   #ftp -n:禁用自动登录.
user ftpadmin "$Password"
binary
bell
cd $Directory
put "$Filename.lsm"
put "$Filename.tar.gz"
bye
EOF
exit 0

在 here document 的开头引用或转义"limit string"会使得 here document 的消息体中的参数替换被禁用。

Example 17-7 关闭参数替换

#!/bin/bash
#
NAME="John Doe"
RESPONDENT="the author of this fine script"

cat << 'EOF'

Hello, there, $NAME.
Greetings to you, $NAME, from $RESPONDENT.

EOF
#当"limit string"被引用或转义那么就禁用了参数替换.
#下边的两种方式具有相同的效果.
#cat << "EOF"
#cat << \EOF
exit 0

禁用了参数替换后,将允许输出文本本身。产生脚本甚至是程序代码就是这种用法的用途之一。

Example 17-8 一个产生另外一个脚本的脚本

#!/bin/bash
#
OUTFILE=generated.sh   # 所产生文件的名字.
# 'Here document 包含了需要产生的脚本的代码.
(
cat <<'EOF'
#!/bin/bash

echo "This is a generated shell script."
# Note that since we are inside a subshell,
#+ we can't access variables in the "outside" script.

echo "Generated file will be named: $OUTFILE"
# Above line will not work as normally expected
#+ because parameter expansion has been disabled.
# Instead, the result is literal output.

a=7
b=3

let "c = $a * $b"
echo "c = $c"

exit 0
EOF
) > $OUTFILE 

if [ -f "$OUTFILE" ];then
  chmod 755 $OUTFILE
else
  echo "Problem in creating file: \"$OUTFILE\""
fi
#这个方法也用来产生 C 程序代码, Perl 程序代码, Python 程序代码, makefile,和其他的一些类似的代码。
exit 0

也可以将 here document 的输出保存到变量中。

[root@localhost shell]# variable=$(cat << EOF
> This variable
> runs over multiple lines.
> EOF
> )
[root@localhost shell]# echo "$variable"
This variable
runs over multiple lines.

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

Example 17-9 Here document与函数

#!/bin/bash
#
GetPersonalData(){
    read firstname
    read lastname
    read address
    read city
    read state
    read zipcode
}   #这个函数无疑的看起来就一个交互函数, 但是...

#给上边的函数提供输入
GetPersonalData << EOF
Bozo
Bozeman
2726 Nondescript Dr.
Baltimore
MD
21226
EOF
echo
echo "$firstname $lastname"
echo "$address"
echo "$city, $state $zipcode"
echo
exit 0

也可以这么使用: 做一个假命令来从一个 here document 中接收输出。这么做事实上就是创建了 一个"匿名"的 here document。

Example 17-10 匿名 here document

#!/bin/bash
#
: << EOF
${HOSTNAME?}${USER?}${MAIL?}   #如果其中一个变量没被设置, 那么就打印错误信息.
EOF
exit 0

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

#!/bin/bash
#

: << EOF
echo "This line will not echo."
This is a comment line missing the "#" prefix.
This is another comment line missing the "#" prefix.

&*@!!++=
The above line will cause no error message,
because the Bash interpreter will ignore it.
EOF

echo "Exit value of above \"EOF\" is $?"

#上边的这种技术当然也可以用来注释掉一段正在使用的代码, 如果你有某些特定调试要求的话.
#这将比对每行都敲入"#"来得方便的多,而且如果你想恢复的话, 还得将添加上的"#"删除掉.

: << EOF
for file in *
do
  cat "$file"
done
EOF
exit 0  

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

Example 17-12 一个自文档化的脚本

#!/bin/bash
#
DOC_REQUEST=70
if [ "$1" = "-h" -o "$1" = "--help" ];then
  echo; echo "Usage: $0 [directory-name]"; echo
  sed --silent -e '/DOCUMENTATIONXX$/,/^DOCUMENTATIONXX$/p' "$0" | sed -e '/DOCUMENTATIONXX$/d'
  exit $DOC_REQUEST
fi

: << DOCUMENTATIONXX
以表格格式列出指定目录的统计信息。
---------------------------------------------------------------
命令行参数给出要列出的目录。
如果没有指定目录或指定的目录无法读取,则列出当前工作目录。

DOCUMENTATIONXX

if [ -z "$1" -o ! -r "$1" ];then
  directory=
else
  directory="$1"
fi

echo "Listing of "$directory":"; echo
(printf "PERMISSIONS LINKS OWNER GROUP SIZE MONTH DAY HH:MM PROG-NAME\n" ; ls -l "$directory" | sed 1d) | column -t
#column -t:判断输入行的列数来创建一个表。
#分隔符是使用在-s中指定的字符。如果没有指定分隔符,默认是空格

exit 0

使用cat脚本也能够完成相同的目的

DOC_REQUEST=70

if [ "$1" = "-h" -o "$1" = "--help" ];then
  cat << DOCUMENTATIONXX
List the statistics of a specified directory in tabular format.
---------------------------------------------------------------
The command line parameter gives the directory to be listed.
If no directory specified or directory specified cannot be read,
then list the current working directory.

DOCUMENTATIONXX
exit $DOC_REQUEST
fi

参见Example A-27 可以了解更多关于自文档化脚本的好例子。

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

注意: 某些工具是不能工作在 here document 中的。

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

#!/bin/bash
#
echo "-----------------------------------------------------------------"
cat << EOF
echo "This is line 1 of the message inside the here document."
echo "This is line 2 of the message inside the here document."
echo "This is the final line of the message inside the here document."
    EOF
echo "-----------------------------------------------------------------"
echo "Outside the here document."
exit 0

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

17.1 Here Strings

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

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

#!/bin/bash
#
E_NOSUCHFILE=65
read -p "File: " file
if [ ! -e  "$file" ];then
  echo "File $file not found."
  exit $E_NOSUCHFILE
fi

read -p "Title: " title
cat - $file <<< $title > $file.new
echo "Modified file is $file.new"
exit 0
#下边是'man bash'中的一段:
# Here Strings
# here document 的一种变形,形式如下:
# <<<word   :word 被扩展并且提供到 command 的标准输入中.

为什么是在行首添加?

第十八章 休息时间

sleep

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值