Chinaunix首页 | 论坛 | 博客
  • 博客访问: 514358
  • 博文数量: 119
  • 博客积分: 5054
  • 博客等级: 大校
  • 技术积分: 1305
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-03 13:13
文章分类

全部博文(119)

文章存档

2011年(4)

2010年(115)

我的朋友

分类:

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 0

here 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 # 		<<
		
		
		
		                                   
阅读(1473) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~