分类:
2010-01-03 22:47:26
摘自《高级bash脚本编程指南》
ps:使用例子中的代码进行测试练习时,请拷贝其中的代码,并保存到名为 test.sh 的文件,然后执行下面的命令去除每行代码前面的序号和空白。
sed -i 's/^\s*[0-9][0-9]*\s\{0,1\}//' test.sh
在 vi 中,保持文本原样粘贴需要打开 paste 选项(命令行模式):
:set paste
打开 paste 选项,似乎相应的也就屏蔽了 autoident 选项功能了,所以在编写源程序时,粘贴完后最好还是关闭 paste 选项功能。
:set nopaste
另:使用vi查看文件内容编码,这在有时需要转换文件编码时确定文件的编码十分方便。(转换文件编码可以使用iconv工具:iconv -f encoding -t encoding -o newfile file)
:set fileencoding
一个 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.
例子 1. 广播: 将消息发送给每个登陆的用户
1 #!/bin/bash 2 3 wall <对于某些看上去不太可能的工具, 比如vi, 也能够使用here document.
例子 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.
例子 3. 使用cat的多行消息
1 #!/bin/bash 2 3 # 'echo'对于打印单行消息来说是非常好用的, 4 #+ 但是在打印消息块时可能就有点问题了. 5 # 'cat' here document可以解决这个限制. 6 7 cat <$Newfile < - 选项用来标记here document的 limit string (<<-LimitString), 可以抑制输出时前边的tab(不是空格). 这么做可以增加一个脚本的可读性.
例子 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 0here document支持参数和命令替换. 所以也可以给here document的消息体传递不同的参数, 这样相应的也会修改输出.
例子 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.
例子 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消息体中的参数替换被禁用.
例子 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禁用了参数替换后, 将允许输出文本本身(译者注: 就是未转义的原文). 如果你想产生脚本甚至是程序代码的话, 那么可以使用这种办法.
例子 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 <A here document can supply input to a function in the same script.
例子 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.
例子 10. “匿名”的here Document
1 #!/bin/bash 2 3 : <: (冒号)作为占位符 4 ${HOSTNAME?}${USER?}${MAIL?} # 如果其中某个变量没被设置, 那么就打印错误信息. 5 TESTVARIABLES 6 7 exit 0 上边所示技术的一种变化, 可以用来”注释“掉代码块.
例子 11. 注释掉一段代码块
1 #!/bin/bash 2 # commentblock.sh 3 4 : <关于这种小技巧的另一个应用就是能够产生”自文档化(self-documenting)“的脚本.
例子 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 <Here document 创建临时文件, 但是这些文件将在打开后被删除, 并且不能够被任何其他进程所访问.
$ bash -c 'lsof -a -p $$ -d0' << EOF > EOF COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME lsof 8395 fhc2007 0r REG 8,3 0 729101 /tmp/sh-thd-1255163095 (deleted)某些工具是不能放入here document中运行的.
注:结尾的 limit string, 就是here document最后一行的limit string, 必须从第一个字符开始. 它的前面不能够有任何前置的空白. 而在这个limit string后边的空白也会引起异常. 空白将会阻止limit string的识别. (译者注: 下边这个脚本由于结束limit string的问题, 造成脚本无法结束, 所有内容全部被打印出来, 所以注释就不译了, 保持这个例子脚本的原样.)
1 #!/bin/bash 2 3 echo "----------------------------------------------------------------------" 4 5 cat <Here String
here string可以看成是here document的一种定制形式. 除了COMMAND <<<$WORD, 就什么都没有了, $WORD将被扩展并且被送入COMMAND的stdin中.
1 String="This is a string of words." 2 3 read -r -a Words <<< "$String" 4 # "read"命令的-a选项 5 #+ 将会把结果值按顺序的分配给数组中的每一项. 6 7 echo "First word in String is: ${Words[0]}" # This 8 echo "Second word in String is: ${Words[1]}" # is 9 echo "Third word in String is: ${Words[2]}" # a 10 echo "Fourth word in String is: ${Words[3]}" # string 11 echo "Fifth word in String is: ${Words[4]}" # of 12 echo "Sixth word in String is: ${Words[5]}" # words. 13 echo "Seventh word in String is: ${Words[6]}" # (null) 14 # $String的结尾. 15 16 # 感谢, Francisco Lobo的这个建议.例子 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 String 26 # here document的一种变形,形式如下: 27 # 28 # <<