linux社区爱心援助Linux认证系列教程业界动态站务新闻公司招聘建议留言网址大全LPI专题CISCO专题
设为首页
加入收藏
管理团队
JSP  
JAVA  
PERL  
 您的位置:首页 > article > Linux开发区 > SHELL >
栏目导栏
资料搜索
热门文章
·csh shell编程入门
·玩转Linux shell命令提示符
·Bourne Shell及shell编程
·Shell 编程实例集锦
·Linux下的shell编程入门
·Shell编程基础
·linux shell 编程基础
·shell基础十二篇
·Linux的Shell编程
·linux Shell编程入门 实例讲解
·Linux主要shell命令详解
·Linux shell 脚本实例一
·深入浅出Shell编程: Shell 变量
·shell命令(一)
·UNIX/LINUX SHELL 正则表达式语
最新文章
·Linux系统中加入自定义Shell为
·Shell学习:关于替换命令-tr-R
·Linux Shell学习:uniq命令使用
·uClinux操作系统下的shell功能
·Shell编程基础:单引号和双引号
·Linux操作系统下Shell语句元字
·Linux系统环境程序设计之路
·Linux Shell中PS命令中的%CPU的
·Linux Shell元字符知识笔记
·压缩命令-vi-认识SHELL-正规表
·Linux系统下Shell命令行快捷键
·谈Linux Shell下的输出重定向
·在Shell中执行vi/cp/mv时自动备
·shell数组介绍
·开启和关闭Shell特性的小技巧
Google
 
高级Bash脚本编程指南(一)
[ 作者:  加入时间:2007-11-28 11:36:57  来自:Linux联盟收集整理 ]

第一部分 热身xJDLinux联盟
++++++++++++++++xJDLinux联盟
shell是一个命令解释器.是介于操作系统kernel与用户之间的一个绝缘层.准确地说,它也是一xJDLinux联盟
一种强力的计算机语言.一个shell程序,被称为一个脚本,是一种很容易使用的工具,它可以通过xJDLinux联盟
将系统调用,公共程序,工具,和编译过的二进制程序粘合在一起来建立应用.事实上,所有的UNIXxJDLinux联盟
命令和工具再加上公共程序,对于shell脚本来说,都是可调用的.如果这些你还觉得不够,那么xJDLinux联盟
shell内建命令,比如test与循环结构,也会给脚本添加强力的支持和增加灵活性.Shell脚本对于xJDLinux联盟
管理系统任务和其它的重复工作的例程来说,表现的非常好,根本不需要那些华而不实的成熟xJDLinux联盟
紧凑的程序语言.xJDLinux联盟
第1章 为什么使用shell编程xJDLinux联盟
===========================xJDLinux联盟
没有程序语言是完美的.甚至没有一个唯一最好的语言,只有对于特定目的,比较适合和不适合xJDLinux联盟
的程序语言.xJDLinux联盟
                Herbert MayerxJDLinux联盟
对于任何想适当精通一些系统管理知识的人来说,掌握shell脚本知识都是最基本的,即使这些xJDLinux联盟
人可能并不打算真正的编写一些脚本.想一下Linux机器的启动过程,在这个过程中,必将运行xJDLinux联盟
/etc/rc.d目录下的脚本来存储系统配置和建立服务.详细的理解这些启动脚本对于分析系统的xJDLinux联盟
行为是非常重要的,并且有时候可能必须修改它.xJDLinux联盟
学习如何编写shell脚本并不是一件很困难的事,因为脚本可以分为很小的块,并且相对于shellxJDLinux联盟
特性的操作和选项[1]部分,只需要学习很小的一部分就可以了.语法是简单并且直观的,编写脚xJDLinux联盟
本很像是在命令行上把一些相关命令和工具连接起来,并且只有很少的一部分规则需要学习.xJDLinux联盟
绝大部分脚本第一次就可以正常的工作,而且即使调试一个长一些的脚本也是很直观的.xJDLinux联盟
一个shell脚本是一个类似于小吃店的(quick and dirty)方法,在你使用原型设计一个复杂的xJDLinux联盟
应用的时候.在工程开发的第一阶段,即使从功能中取得很有限的一个子集放到shell脚本中来xJDLinux联盟
完成往往都是非常有用的.使用这种方法,程序的结果可以被测试和尝试运行,并且在处理使用xJDLinux联盟
诸如C/C++,Java或者Perl语言编写的最终代码前,主要的缺陷和陷阱往往就被发现了.xJDLinux联盟
Shell脚本遵循典型的UNIX哲学,就是把大的复杂的工程分成小规模的子任务,并且把这些部件xJDLinux联盟
和工具组合起来.许多人认为这种办法更好一些,至少这种办法比使用那种高\大\全的语言更xJDLinux联盟
美,更愉悦,更适合解决问题.比如Perl就是这种能干任何事能适合任何人的语言,但是代价就是xJDLinux联盟
你需要强迫自己使用这种语言来思考解决问题的办法.xJDLinux联盟
什么时候不使用Shell脚本xJDLinux联盟
 xJDLinux联盟
 资源密集型的任务,尤其在需要考虑效率时(比如,排序,hash等等)xJDLinux联盟
 需要处理大任务的数学操作,尤其是浮点运算,精确运算,或者复杂的算术运算xJDLinux联盟
 (这种情况一般使用C++或FORTRAN来处理)xJDLinux联盟
 有跨平台移植需求(一般使用C或Java)xJDLinux联盟
 复杂的应用,在必须使用结构化编程的时候(需要变量的类型检查,函数原型,等等)xJDLinux联盟
 对于影响系统全局性的关键任务应用。xJDLinux联盟
 对于安全有很高要求的任务,比如你需要一个健壮的系统来防止入侵,破解,恶意破坏等等.xJDLinux联盟
 项目由连串的依赖的各个部分组成。xJDLinux联盟
 需要大规模的文件操作xJDLinux联盟
 需要多维数组的支持xJDLinux联盟
 需要数据结构的支持,比如链表或数等数据结构xJDLinux联盟
 需要产生或操作图形化界面GUIxJDLinux联盟
 需要直接操作系统硬件xJDLinux联盟
 需要I/O或socket接口xJDLinux联盟
 需要使用库或者遗留下来的老代码的接口xJDLinux联盟
 私人的,闭源的应用(shell脚本把代码就放在文本文件中,全世界都能看到)xJDLinux联盟
如果你的应用符合上边的任意一条,那么就考虑一下更强大的语言吧--或许是Perl,Tcl,Python,xJDLinux联盟
Ruby -- 或者是更高层次的编译语言比如C/C++,或者是Java.即使如此,你会发现,使用shellxJDLinux联盟
来原型开发你的应用,在开发步骤中也是非常有用的.xJDLinux联盟
我们将开始使用Bash,Bash是"Bourne-Again shell"首字母的缩写,也是Stephen Bourne的经典xJDLinux联盟
的Bourne shell的一个双关语,(译者:说实话,我一直搞不清这个双关语是什么意思,为什么叫xJDLinux联盟
"Bourn-Again shell",这其中应该有个什么典故吧,哪位好心,告诉我一下^^).Bash已经成为了xJDLinux联盟
所有UNIX中shell脚本的事实上的标准了.同时这本书也覆盖了绝大部分的其他一些shell的原xJDLinux联盟
则,比如Korn Shell,Bash从ksh中继承了一部分特性,[2]C Shell和它的变种.(注意:C ShellxJDLinux联盟
编程是不被推荐的,因为一些特定的内在问题,Tom Christiansen在1993年10月指出了这个问题xJDLinux联盟
请在http://www.etext.org/Quartz/computer/unix/csh.harmful.gz中查看具体内容.)xJDLinux联盟
接下来是脚本的一些说明.在展示shell不同的特征之前,它可以减轻一些阅读书中例子xJDLinux联盟
的负担.本书中的例子脚本,都在尽可能的范围内进行了测试,并且其中的一些将使用在真xJDLinux联盟
实的生活中.读者可以运行这些例子脚本(使用scriptname.sh或者scriptname.bash的形式),xJDLinux联盟
[3]并给这些脚本执行权限(chmod u+rx scriptname),然后执行它们,看看发生了什么.如果存xJDLinux联盟
档的脚本不可用,那么就从本书的HTML,pdf或者text的发行版本中把它们拷贝粘贴出来.考虑到xJDLinux联盟
这些脚本中的内容在我们还没解释它之前就被列在这里,可能会影响读者的理解,这就需要读者xJDLinux联盟
暂时忽略这些内容.xJDLinux联盟
除非特别注明,本书作者编写了本书中的绝大部分例子脚本.xJDLinux联盟
注意事项:xJDLinux联盟
[1]  这些在builtins章节被引用,这些是shell的内部特征.xJDLinux联盟
[2]  ksh88的许多特性,甚至是一些ksh93的特性都被合并到Bash中了.xJDLinux联盟
[3]  根据惯例,用户编写的Bourne shell脚本应该在脚本的名字后边加上.sh扩展名.xJDLinux联盟
  一些系统脚本,比如那些在/etc/rc.d中的脚本,则不遵循这种命名习惯.xJDLinux联盟
 xJDLinux联盟
第2章 带着一个Sha-Bang出发(Sha-Bang指的是#!)xJDLinux联盟
==============================================xJDLinux联盟
在一个最简单的例子中,一个shell脚本其实就是将一堆系统命令列在一个文件中.它的最基本的xJDLinux联盟
用处就是,在你每次输入这些特定顺序的命令时可以少敲一些字.xJDLinux联盟
Example 2-1 清除:清除/var/log下的log文件xJDLinux联盟
################################Start Script#######################################xJDLinux联盟
1 # CleanupxJDLinux联盟
2 # 当然要使用root身份来运行这个脚本xJDLinux联盟
3 xJDLinux联盟
4 cd /var/logxJDLinux联盟
5 cat /dev/null > messagesxJDLinux联盟
6 cat /dev/null > wtmpxJDLinux联盟
7 echo "Logs cleaned up."xJDLinux联盟
################################End Script#########################################xJDLinux联盟
这根本就没什么稀奇的, 只不过是命令的堆积, 来让从console或者xterm中一个一个的输入命xJDLinux联盟
令更方便一些.好处就是把所有命令都放在一个脚本中,不用每次都敲它们.这样的话,对于特定xJDLinux联盟
的应用来说,这个脚本就很容易被修改或定制.xJDLinux联盟
Example 2-2 清除:一个改良的清除脚本xJDLinux联盟
################################Start Script#######################################xJDLinux联盟
 1 #!/bin/bashxJDLinux联盟
 2 # 一个Bash脚本的正确的开头部分.xJDLinux联盟
 3 xJDLinux联盟
 4 # Cleanup, 版本 2xJDLinux联盟
 5 xJDLinux联盟
 6 # 当然要使用root身份来运行.xJDLinux联盟
 7 # 在此处插入代码,来打印错误消息,并且在不是root身份的时候退出.xJDLinux联盟
 8 xJDLinux联盟
 9 LOG_DIR=/var/logxJDLinux联盟
10 # 如果使用变量,当然比把代码写死的好.xJDLinux联盟
11 cd $LOG_DIRxJDLinux联盟
12 xJDLinux联盟
13 cat /dev/null > messagesxJDLinux联盟
14 cat /dev/null > wtmpxJDLinux联盟
15 xJDLinux联盟
16 xJDLinux联盟
17 echo "Logs cleaned up."xJDLinux联盟
18 xJDLinux联盟
19 exit # 这个命令是一种正确并且合适的退出脚本的方法.xJDLinux联盟
################################End Script#########################################xJDLinux联盟
现在,让我们看一下一个真正意义的脚本.而且我们可以走得更远...xJDLinux联盟
Example 2-3. cleanup:一个增强的和广义的删除logfile的脚本xJDLinux联盟
################################Start Script#######################################xJDLinux联盟
 1 #!/bin/bashxJDLinux联盟
 2 # 清除, 版本 3xJDLinux联盟
 3 xJDLinux联盟
 4 #  Warning:xJDLinux联盟
 5 #  -------xJDLinux联盟
 6 #  这个脚本有好多特征,这些特征是在后边章节进行解释的,大概是进行到本书的一半的xJDLinux联盟
 7 #  时候,xJDLinux联盟
 8 #  你就会觉得它没有什么神秘的了.xJDLinux联盟
 9 #xJDLinux联盟
10 xJDLinux联盟
11 xJDLinux联盟
12 xJDLinux联盟
13 LOG_DIR=/var/logxJDLinux联盟
14 ROOT_UID=0     # $UID为0的时候,用户才具有根用户的权限xJDLinux联盟
15 LINES=50       # 默认的保存行数xJDLinux联盟
16 E_XCD=66       # 不能修改目录?xJDLinux联盟
17 E_NOTROOT=67   # 非根用户将以error退出xJDLinux联盟
18 xJDLinux联盟
19 xJDLinux联盟
20 # 当然要使用根用户来运行xJDLinux联盟
21 if [ "$UID" -ne "$ROOT_UID" ]xJDLinux联盟
22 thenxJDLinux联盟
23   echo "Must be root to run this script."xJDLinux联盟
24   exit $E_NOTROOTxJDLinux联盟
25 fi  xJDLinux联盟
26 xJDLinux联盟
27 if [ -n "$1" ]xJDLinux联盟
28 # 测试是否有命令行参数(非空).xJDLinux联盟
29 thenxJDLinux联盟
30   lines=$1xJDLinux联盟
31 else  xJDLinux联盟
32   lines=$LINES # 默认,如果不在命令行中指定xJDLinux联盟
33 fi  xJDLinux联盟
34 xJDLinux联盟
35 xJDLinux联盟
36 #  Stephane Chazelas 建议使用下边xJDLinux联盟
37 #+ 的更好方法来检测命令行参数.xJDLinux联盟
38 #+ 但对于这章来说还是有点超前.xJDLinux联盟
39 #xJDLinux联盟
40 #    E_WRONGARGS=65  # 非数值参数(错误的参数格式)xJDLinux联盟
41 #xJDLinux联盟
42 #    case "$1" inxJDLinux联盟
43 #    ""      ) lines=50;;xJDLinux联盟
44 #    *[!0-9]*) echo "Usage: `basename $0` file-to-cleanup"; exit $E_WRONGARGS;;xJDLinux联盟
45 #    *       ) lines=$1;;xJDLinux联盟
46 #    esacxJDLinux联盟
47 #xJDLinux联盟
48 #* 直到"Loops"的章节才会对上边的内容进行详细的描述.xJDLinux联盟
49 xJDLinux联盟
50 xJDLinux联盟
51 cd $LOG_DIRxJDLinux联盟
52 xJDLinux联盟
53 if [ `pwd` != "$LOG_DIR" ]  # 或者 if[ "$PWD" != "$LOG_DIR" ]xJDLinux联盟
54                             # 不在 /var/log中?xJDLinux联盟
55 thenxJDLinux联盟
56   echo "Can't change to $LOG_DIR."xJDLinux联盟
57   exit $E_XCDxJDLinux联盟
58 fi  # 在处理log file之前,再确认一遍当前目录是否正确.xJDLinux联盟
59 xJDLinux联盟
60 # 更有效率的做法是xJDLinux联盟
61 #xJDLinux联盟
62 # cd /var/log || {xJDLinux联盟
63 #   echo "Cannot change to necessary directory." >&2xJDLinux联盟
64 #   exit $E_XCD;xJDLinux联盟
65 # }xJDLinux联盟
66 xJDLinux联盟
67 xJDLinux联盟
68 xJDLinux联盟
69 xJDLinux联盟
70 tail -$lines messages > mesg.temp # 保存log file消息的最后部分.xJDLinux联盟
71 mv mesg.temp messages             # 变为新的log目录.xJDLinux联盟
72 xJDLinux联盟
73 xJDLinux联盟
74 # cat /dev/null > messagesxJDLinux联盟
75 #* 不再需要了,使用上边的方法更安全.xJDLinux联盟
76 xJDLinux联盟
77 cat /dev/null > wtmp  #  ': > wtmp' 和 '> wtmp'具有相同的作用xJDLinux联盟
78 echo "Logs cleaned up."xJDLinux联盟
79 xJDLinux联盟
80 exit 0xJDLinux联盟
81 #  退出之前返回0,返回0表示成功.xJDLinux联盟
82 #xJDLinux联盟
################################End Script#########################################xJDLinux联盟
因为你可能希望将系统log全部消灭,这个版本留下了log消息最后的部分.你将不断地找到新xJDLinux联盟
的方法来完善这个脚本,并提高效率.xJDLinux联盟
要注意,在每个脚本的开头都使用"#!",这意味着告诉你的系统这个文件的执行需要指定一个解xJDLinux联盟
释器.#!实际上是一个2字节[1]的魔法数字,这是指定一个文件类型的特殊标记, 换句话说, 在xJDLinux联盟
这种情况下,指的就是一个可执行的脚本(键入man magic来获得关于这个迷人话题的更多详细xJDLinux联盟
信息).在#!之后接着是一个路径名.这个路径名指定了一个解释脚本中命令的程序,这个程序可xJDLinux联盟
以是shell,程序语言或者是任意一个通用程序.这个指定的程序从头开始解释并且执行脚本中xJDLinux联盟
的命令(从#!行下边的一行开始),忽略注释.[2]xJDLinux联盟
如:xJDLinux联盟
1 #!/bin/shxJDLinux联盟
2 #!/bin/bashxJDLinux联盟
3 #!/usr/bin/perlxJDLinux联盟
4 #!/usr/bin/tclxJDLinux联盟
5 #!/bin/sed -fxJDLinux联盟
6 #!/usr/awk -fxJDLinux联盟
上边每一个脚本头的行都指定了一个不同的命令解释器,如果是/bin/sh,那么就是默认shellxJDLinux联盟
(在Linux系统中默认是Bash).[3]使用#!/bin/sh,在大多数商业发行的UNIX上,默认是BournexJDLinux联盟
shell,这将让你的脚本可以正常的运行在非Linux机器上,虽然这将会牺牲Bash一些独特的特征.xJDLinux联盟
脚本将与POSIX[4] 的sh标准相一致.xJDLinux联盟
注意: #! 后边给出的路径名必须是正确的,否则将会出现一个错误消息,通常是xJDLinux联盟
"Command not found",这将是你运行这个脚本时所得到的唯一结果.xJDLinux联盟
当然"#!"也可以被忽略,不过这样你的脚本文件就只能是一些命令的集合,不能够使用shell内建xJDLinux联盟
的指令了,如果不能使用变量的话,当然这也就失去了脚本编程的意义了.xJDLinux联盟
 注意:这个例子鼓励你使用模块化的方式来编写脚本,平时也要注意收集一些零碎的代码,xJDLinux联盟
  这些零碎的代码可能用在你将来编写的脚本中.这样你就可以通过这些代码片段来构xJDLinux联盟
  造一个较大的工程用例. 以下边脚本作为序,来测试脚本被调用的参数是否正确.xJDLinux联盟
################################Start Script#######################################xJDLinux联盟
 1 E_WRONG_ARGS=65xJDLinux联盟
 2 script_parameters="-a -h -m -z"xJDLinux联盟
 3 #                  -a = all, -h = help, 等等.xJDLinux联盟
 4 xJDLinux联盟
 5 if [ $# -ne $Number_of_expected_args ]xJDLinux联盟
 6 thenxJDLinux联盟
 7   echo "Usage: `basename $0` $script_parameters"xJDLinux联盟
 8   # `basename $0`是这个脚本的文件名xJDLinux联盟
 9   exit $E_WRONG_ARGSxJDLinux联盟
10 fixJDLinux联盟
################################End Script#########################################xJDLinux联盟
大多数情况下,你需要编写一个脚本来执行一个特定的任务,在本章中第一个脚本就是一个这样xJDLinux联盟
的例子, 然后你会修改它来完成一个不同的,但比较相似的任务.用变量来代替写死的常量,就是xJDLinux联盟
一个好方法,将重复的代码放到一个函数中,也是一种好习惯.

2.1 调用一个脚本xJDLinux联盟
----------------xJDLinux联盟
编写完脚本之后,你可以使用sh scriptname,[5]或者bash scriptname来调用它.xJDLinux联盟
(不推荐使用sh <scriptname,因为这禁用了脚本从stdin中读数据的功能.)xJDLinux联盟
更方便的方法是让脚本本身就具有可执行权限,通过chmod命令可以修改.xJDLinux联盟
比如:xJDLinux联盟
    chmod 555 scriptname (允许任何人都具有 可读和执行权限) [6]  xJDLinux联盟
或:xJDLinux联盟
 chmod +rx scriptname (允许任何人都具有 可读和执行权限)xJDLinux联盟
 chmod u+rx scriptname (只给脚本的所有者 可读和执行权限)xJDLinux联盟
既然脚本已经具有了可执行权限,现在你可以使用./scriptname.[7]来测试它了.如果这个脚本xJDLinux联盟
以一个"#!"行开头,那么脚本将会调用合适的命令解释器来运行.xJDLinux联盟
最后一步,在脚本被测试和debug之后,你可能想把它移动到/usr/local/bin(当然是以root身份)xJDLinux联盟
,来让你的脚本对所有用户都有用.这样用户就可以直接敲脚本名字来运行了.xJDLinux联盟
注意事项:xJDLinux联盟
[1]  那些具有UNIX味道的脚本(基于4.2BSD)需要一个4字节的魔法数字,在#!后边需要一个xJDLinux联盟
  空格#! /bin/sh.xJDLinux联盟
[2]  脚本中的#!行的最重要的任务就是命令解释器(sh或者bash).因为这行是以#开始的,xJDLinux联盟
  当命令解释器执行这个脚本的时候,会把它作为一个注释行.当然,在这之前,这行语句xJDLinux联盟
  已经完成了它的任务,就是调用命令解释器.xJDLinux联盟
  如果在脚本的里边还有一个#!行,那么bash将把它认为是一个一般的注释行.xJDLinux联盟
   1 #!/bin/bashxJDLinux联盟
   2 xJDLinux联盟
   3 echo "Part 1 of script."xJDLinux联盟
   4 a=1xJDLinux联盟
   5 xJDLinux联盟
   6 #!/bin/bashxJDLinux联盟
   7 # 这将不会开始一个新脚本.xJDLinux联盟
   8 xJDLinux联盟
   9 echo "Part 2 of script."xJDLinux联盟
  10 echo $a  # Value of $a stays at 1.xJDLinux联盟
[3]  这里可以玩一些小技巧.xJDLinux联盟
   1 #!/bin/rmxJDLinux联盟
   2 # 自删除脚本.xJDLinux联盟
   3 xJDLinux联盟
   4 # 当你运行这个脚本时,基本上什么都不会发生...除非这个文件消失不见.xJDLinux联盟
   5 xJDLinux联盟
   6 WHATEVER=65xJDLinux联盟
   7 xJDLinux联盟
   8 echo "This line will never print (betcha!)."xJDLinux联盟
   9 xJDLinux联盟
  10 exit $WHATEVER  # 没关系,脚本是不会在这退出的.xJDLinux联盟
  当然,你还可以试试在一个README文件的开头加上#!/bin/more,并让它具有执行权限.xJDLinux联盟
  结果将是文档自动列出自己的内容.(一个使用cat命令的here document可能是一个xJDLinux联盟
  更好的选则,--见Example 17-3).xJDLinux联盟
[4]  可移植的操作系统接口,标准化类UNIX操作系统的一种尝试.POSIX规范可以在xJDLinux联盟
  http://www.opengroup.org/onlinepubs/007904975/toc.htm中查阅.xJDLinux联盟
[5]  小心:使用sh scriptname来调用脚本的时候将会关闭一些Bash特定的扩展,脚本可能xJDLinux联盟
  因此而调用失败.xJDLinux联盟
[6]  脚本需要读和执行权限,因为shell需要读这个脚本.xJDLinux联盟
[7]  为什么不直接使用scriptname来调用脚本?如果你当前的目录下($PWD)正好有你想要xJDLinux联盟
  执行的脚本,为什么它运行不了呢?失败的原因是,出于安全考虑,当前目录并没有被xJDLinux联盟
  加在用户的$PATH变量中.因此,在当前目录下调用脚本必须使用./scriptname这种xJDLinux联盟
  形式.

2.2 初步的练习xJDLinux联盟
--------------xJDLinux联盟
1. 系统管理员经常会为了自动化一些常用的任务而编写脚本.举出几个这种有用的脚本的实例.xJDLinux联盟
2. 编写一个脚本,显示时间和日期,列出所有的登录用户,显示系统的更新时间.然后这个脚本xJDLinux联盟
   将会把这些内容保存到一个log file中.

第二部分 基本xJDLinux联盟
++++++++++++++++xJDLinux联盟
第3章 特殊字符xJDLinux联盟
================xJDLinux联盟
#  注释,行首以#开头为注释(#!是个例外).xJDLinux联盟
  1 # This line is a comment.xJDLinux联盟
  注释也可以存在于本行命令的后边.xJDLinux联盟
  1 echo "A comment will follow." # 注释在这里xJDLinux联盟
  2 #                            ^ 注意#前边的空白xJDLinux联盟
  注释也可以在本行空白的后边.xJDLinux联盟
  1  # A tab precedes this comment.xJDLinux联盟
  注意:命令是不能跟在同一行上注释的后边的,没有办法,在同一行上,注释的后边想xJDLinux联盟
  要再使用命令,只能另起一行.xJDLinux联盟
  当然,在echo命令中被转义的#是不能作为注释的. xJDLinux联盟
  同样的,#也可以出现在特定的参数替换结构中或者是数字常量表达式中.xJDLinux联盟
  1 echo "The # here does not begin a comment."xJDLinux联盟
  2 echo 'The # here does not begin a comment.'xJDLinux联盟
  3 echo The \# here does not begin a comment.xJDLinux联盟
  4 echo The # 这里开始一个注释xJDLinux联盟
  5 xJDLinux联盟
  6 echo ${PATH#*:}       # 参数替换,不是一个注释xJDLinux联盟
  7 echo $(( 2#101011 ))  # 数制转换,不是一个注释xJDLinux联盟
  8 xJDLinux联盟
  9 # Thanks, S.C.xJDLinux联盟
  标准的引用和转义字符("'\)可以用来转义#xJDLinux联盟
;  命令分隔符,可以用来在一行中来写多个命令.xJDLinux联盟
  1 echo hello; echo therexJDLinux联盟
  2 xJDLinux联盟
  3 xJDLinux联盟
  4 if [ -x "$filename" ]; then    # 注意:"if"和"then"需要分隔xJDLinux联盟
  5                                # 为啥?xJDLinux联盟
  6   echo "File $filename exists."; cp $filename $filename.bakxJDLinux联盟
  7 elsexJDLinux联盟
  8   echo "File $filename not found."; touch $filenamexJDLinux联盟
  9 fi; echo "File test complete."xJDLinux联盟
  有时候需要转义xJDLinux联盟
;;  终止"case"选项.xJDLinux联盟
  1 case "$variable" inxJDLinux联盟
  2 abc)  echo "\$variable = abc" ;;xJDLinux联盟
  3 xyz)  echo "\$variable = xyz" ;;xJDLinux联盟
  4 esacxJDLinux联盟
.  .命令等价于source命令(见Example 11-20).这是一个bash的内建命令.xJDLinux联盟
.  .作为文件名的一部分.如果作为文件名的前缀的话,那么这个文件将成为隐藏文件.xJDLinux联盟
  将不被ls命令列出.xJDLinux联盟
  bash$ touch .hidden-filexJDLinux联盟
  bash$ ls -l       xJDLinux联盟
  total 10xJDLinux联盟
  -rw-r--r--    1 bozo      4034 Jul 18 22:04 data1.addressbookxJDLinux联盟
  -rw-r--r--    1 bozo      4602 May 25 13:58 data1.addressbook.bakxJDLinux联盟
  -rw-r--r--    1 bozo       877 Dec 17  2000 employment.addressbookxJDLinux联盟
  xJDLinux联盟
  xJDLinux联盟
  bash$ ls -al       xJDLinux联盟
  total 14xJDLinux联盟
  drwxrwxr-x    2 bozo  bozo      1024 Aug 29 20:54 ./xJDLinux联盟
  drwx------   52 bozo  bozo      3072 Aug 29 20:51 ../xJDLinux联盟
  -rw-r--r--    1 bozo  bozo      4034 Jul 18 22:04 data1.addressbookxJDLinux联盟
  -rw-r--r--    1 bozo  bozo      4602 May 25 13:58 data1.addressbook.bakxJDLinux联盟
  -rw-r--r--    1 bozo  bozo       877 Dec 17  2000 employment.addressbookxJDLinux联盟
  -rw-rw-r--    1 bozo  bozo         0 Aug 29 20:54 .hidden-filexJDLinux联盟
  .命令如果作为目录名的一部分的话,那么.表达的是当前目录.".."表示上一级目录.xJDLinux联盟
  bash$ pwdxJDLinux联盟
  /home/bozo/projectsxJDLinux联盟
  bash$ cd .xJDLinux联盟
  bash$ pwdxJDLinux联盟
  /home/bozo/projectsxJDLinux联盟
  bash$ cd ..xJDLinux联盟
  bash$ pwdxJDLinux联盟
  /home/bozo/xJDLinux联盟
  .命令经常作为一个文件移动命令的目的地.xJDLinux联盟
  bash$ cp /home/bozo/current_work/junk/* .xJDLinux联盟
.  .字符匹配,这是作为正则表达是的一部分,用来匹配任何的单个字符.xJDLinux联盟
"  部分引用."STRING"阻止了一部分特殊字符,具体见第5章.xJDLinux联盟
'  全引用. 'STRING' 阻止了全部特殊字符,具体见第5章.xJDLinux联盟
,  逗号链接了一系列的算术操作,虽然里边所有的内容都被运行了,但只有最后一项被xJDLinux联盟
  返回.xJDLinux联盟
  如:xJDLinux联盟
  1 let "t2 = ((a = 9, 15 / 3))"  # Set "a = 9" and "t2 = 15 / 3"xJDLinux联盟
\  转义字符,如\X等价于"X"或'X',具体见第5章.xJDLinux联盟
/  文件名路径分隔符.或用来做除法操作.xJDLinux联盟
`  后置引用,命令替换,具体见第14章xJDLinux联盟
:  空命令,等价于"NOP"(no op,一个什么也不干的命令).也可以被认为与shell的内建命令(true)作用相同.":"命令是一xJDLinux联盟
  个bash的内建命令,它的返回值为0,就是shell返回的true.xJDLinux联盟
  如:xJDLinux联盟
  1 :xJDLinux联盟
  2 echo $?   # 0xJDLinux联盟
  死循环,如:xJDLinux联盟
   1 while :xJDLinux联盟
   2 doxJDLinux联盟
   3    operation-1xJDLinux联盟
   4    operation-2xJDLinux联盟
   5    ...xJDLinux联盟
   6    operation-nxJDLinux联盟
   7 donexJDLinux联盟
   8 xJDLinux联盟
   9 # 与下边相同:xJDLinux联盟
  10 #    while truexJDLinux联盟
  11 #    doxJDLinux联盟
  12 #      ...xJDLinux联盟
  13 #    donexJDLinux联盟
  在if/then中的占位符,如:xJDLinux联盟
  1 if conditionxJDLinux联盟
  2 then :   # 什么都不做,引出分支.xJDLinux联盟
  3 elsexJDLinux联盟
  4    take-some-actionxJDLinux联盟
  5 fixJDLinux联盟
  在一个2元命令中提供一个占位符,具体见Example 8-2,和"默认参数".如:xJDLinux联盟
  1 : ${username=`whoami`}xJDLinux联盟
  2 # ${username=`whoami`}   如果没有":"的话,将给出一个错误,除非"username"是xJDLinux联盟
  3 #                        个命令xJDLinux联盟
  在here document中提供一个占位符,见Example 17-10.xJDLinux联盟
  使用"参数替换"来评估字符串变量(见Example 9-14).如:xJDLinux联盟
  1 : ${HOSTNAME?} ${USER?} ${MAIL?}xJDLinux联盟
  2 #  如果一个或多个必要的环境变量没被设置的话,xJDLinux联盟
  3 #+ 就打印错误信息.xJDLinux联盟
  "变量扩展/子串替换"xJDLinux联盟
  在和 > (重定向操作符)结合使用时,把一个文件截断到0长度,没有修改它的权限.xJDLinux联盟
  如果文件在之前并不存在,那么就创建它.如:xJDLinux联盟
  1 : > data.xxx  #文件"data.xxx"现在被清空了.xJDLinux联盟
  2xJDLinux联盟
  3 #与 cat /dev/null >data.xxx 的作用相同xJDLinux联盟
  4 #然而,这不会产生一个新的进程,因为":"是一个内建命令.xJDLinux联盟
  具体参见Example 12-14.xJDLinux联盟
  在和>>重定向操作符结合使用时,将不会对想要附加的文件产生任何影响.xJDLinux联盟
  如果文件不存在,将创建.xJDLinux联盟
  注意: 这只适用于正规文件,而不是管道,符号连接,和某些特殊文件.xJDLinux联盟
  也可能用来作为注释行,虽然我们不推荐这么做.使用#来注释的话,将关闭剩余行的xJDLinux联盟
  错误检查,所以可以在注释行中写任何东西.然而,使用:的话将不会这样.如:xJDLinux联盟
  1 : This is a comment thar generates an error,(if [ $x -eq 3] ).xJDLinux联盟
  ":"还用来在/etc/passwd和$PATH变量中用来做分隔符.xJDLinux联盟
  bash$ echo $PATHxJDLinux联盟
  /usr/local/bin:/bin:/usr/X11R6/bin:/sbin:/usr/sbin:/usr/gamesxJDLinux联盟
!  取反操作符,将反转"退出状态"结果,(见Example 6-2).也会反转test操作符的意义.比xJDLinux联盟
  如修改=为!=.!操作是Bash的一个关键字.xJDLinux联盟
  在一个不同的上下文中,!也会出现在"间接变量引用"见Example 9-22.xJDLinux联盟
  在另一种上下文中,!还能反转bash的"history mechanism"(见附录J 历史命令)xJDLinux联盟
  需要注意的是,在一个脚本中,"history mechanism"是被禁用的.xJDLinux联盟
*  万能匹配字符,用于文件名匹配(这个东西有个专有名词叫file globbing),或者是正则xJDLinux联盟
  表达式中.注意:在正则表达式匹配中的作用和在文件名匹配中的作用是不同的.xJDLinux联盟
  bash$ echo *xJDLinux联盟
  abs-book.sgml add-drive.sh agram.sh alias.shxJDLinux联盟
*  数学乘法.xJDLinux联盟
  **是幂运算.xJDLinux联盟
?  测试操作.在一个确定的表达式中,用?来测试结果.xJDLinux联盟
  (())结构可以用来做数学计算或者是写c代码,那?就是c语言的3元操作符的xJDLinux联盟
  一个.xJDLinux联盟
  在"参数替换"中,?测试一个变量是否被set了.xJDLinux联盟
?  在file globbing中和在正则表达式中一样匹配任意的单个字符.xJDLinux联盟
$  变量替换xJDLinux联盟
  1 var1=5xJDLinux联盟
  2 var2=23skidooxJDLinux联盟
  3 xJDLinux联盟
  4 echo $var1     # 5xJDLinux联盟
  5 echo $var2     # 23skidooxJDLinux联盟
$  在正则表达式中作为行结束符.xJDLinux联盟
${}  参数替换,见9.3节.xJDLinux联盟
$*,$@ 位置参数xJDLinux联盟
$?  退出状态变量.$?保存一个命令/一个函数或者脚本本身的退出状态.xJDLinux联盟
$$  进程ID变量.这个$$变量保存运行脚本进程IDxJDLinux联盟
()  命令组.如:xJDLinux联盟
  1 (a=hello;echo $a)xJDLinux联盟
  注意:在()中的命令列表,将作为一个子shell来运行.xJDLinux联盟
  在()中的变量,由于是在子shell中,所以对于脚本剩下的部分是不可用的.xJDLinux联盟
  如:xJDLinux联盟
  1 a=123xJDLinux联盟
  2 ( a=321; )       xJDLinux联盟
  3 xJDLinux联盟
  4 echo "a = $a"   # a = 123xJDLinux联盟
  5 # 在圆括号中a变量,更像是一个局部变量.xJDLinux联盟
  用在数组初始化,如:xJDLinux联盟
  1 Array=(element1,element2,element3)xJDLinux联盟
{xxx,yyy,zzz...}xJDLinux联盟
  大括号扩展,如:xJDLinux联盟
  1 cat {file1,file2,file3} > combined_filexJDLinux联盟
  2 # 把file1,file2,file3连接在一起,并且重定向到combined_file中.xJDLinux联盟
  3 xJDLinux联盟
  4 xJDLinux联盟
  5 cp file22.{txt,backup}xJDLinux联盟
  6 # 拷贝"file22.txt" 到"file22.backup"中xJDLinux联盟
  一个命令可能会对大括号中的以逗号分割的文件列表起作用[1]. file globbing将对xJDLinux联盟
  大括号中的文件名作扩展.xJDLinux联盟
  注意: 在大括号中,不允许有空白,除非这个空白是有意义的.xJDLinux联盟
  echo {file1,file2}\ :{\ A," B",' C'}xJDLinux联盟
  file1 : A file1 : B file1 : C file2 : A file2 : B file2 : CxJDLinux联盟
{}  代码块.又被称为内部组.事实上,这个结构创建了一个匿名的函数.但是与函数不同的xJDLinux联盟
  是,在其中声明的变量,对于脚本其他部分的代码来说还是可见的.如:xJDLinux联盟
  bash$ xJDLinux联盟
  {xJDLinux联盟
   local a;xJDLinux联盟
   a= 123;xJDLinux联盟
  }xJDLinux联盟
  bash中的local申请的变量只能够用在函数中.xJDLinux联盟
  1 a=123xJDLinux联盟
  2 { a=321; }xJDLinux联盟
  3 echo "a = $a"   # a = 321   (说明在代码块中对变量a所作的修改,影响了外边的变量a)xJDLinux联盟
  4 xJDLinux联盟
  5 # Thanks, S.C.xJDLinux联盟
  下边的代码展示了在{}结构中代码的I/O重定向.xJDLinux联盟
Example 3-1. 代码块和I/O重定向xJDLinux联盟
################################Start Script#######################################xJDLinux联盟
 1 #!/bin/bashxJDLinux联盟
 2 # 从 /etc/fstab中读行xJDLinux联盟
 3 xJDLinux联盟
 4 File=/etc/fstabxJDLinux联盟
 5 xJDLinux联盟
 6 {xJDLinux联盟
 7 read line1xJDLinux联盟
 8 read line2xJDLinux联盟
 9 } < $FilexJDLinux联盟
10 xJDLinux联盟
11 echo "First line in $File is:"xJDLinux联盟
12 echo "$line1"xJDLinux联盟
13 echoxJDLinux联盟
14 echo "Second line in $File is:"xJDLinux联盟
15 echo "$line2"xJDLinux联盟
16 xJDLinux联盟
17 exit 0xJDLinux联盟
18 xJDLinux联盟
19 # 现在,你怎么分析每行的分割域xJDLinux联盟
20 # 暗示: 使用 awk.xJDLinux联盟
################################End Script#########################################xJDLinux联盟
Example 3-2. 将一个代码块的结果保存到文件xJDLinux联盟
################################Start Script#######################################xJDLinux联盟
 1 #!/bin/bashxJDLinux联盟
 2 # rpm-check.shxJDLinux联盟
 3 xJDLinux联盟
 4 # 这个脚本的目的是为了描述,列表,和确定是否可以安装一个rpm包.xJDLinux联盟
 5 # 在一个文件中保存输出.xJDLinux联盟
 6 # xJDLinux联盟
 7 # 这个脚本使用一个代码块来展示xJDLinux联盟
 8 xJDLinux联盟
 9 SUCCESS=0xJDLinux联盟
10 E_NOARGS=65xJDLinux联盟
11 xJDLinux联盟
12 if [ -z "$1" ]xJDLinux联盟
13 thenxJDLinux联盟
14   echo "Usage: `basename $0` rpm-file"xJDLinux联盟
15   exit $E_NOARGSxJDLinux联盟
16 fi  xJDLinux联盟
17 xJDLinux联盟
18 { xJDLinux联盟
19   echoxJDLinux联盟
20   echo "Archive Description:"xJDLinux联盟
21   rpm -qpi $1       # 查询说明xJDLinux联盟
22   echoxJDLinux联盟
23   echo "Archive Listing:"xJDLinux联盟
24   rpm -qpl $1       # 查询列表xJDLinux联盟
25   echoxJDLinux联盟
26   rpm -i --test $1  # 查询rpm包是否可以被安装xJDLinux联盟
27   if [ "$?" -eq $SUCCESS ]xJDLinux联盟
28   thenxJDLinux联盟
29     echo "$1 can be installed."xJDLinux联盟
30   elsexJDLinux联盟
31     echo "$1 cannot be installed."xJDLinux联盟
32   fi  xJDLinux联盟
33   echoxJDLinux联盟
34 } > "$1.test"       # 把代码块中的所有输出都重定向到文件中xJDLinux联盟
35 xJDLinux联盟
36 echo "Results of rpm test in file $1.test"xJDLinux联盟
37 xJDLinux联盟
38 # 查看rpm的man页来查看rpm的选项xJDLinux联盟
39 xJDLinux联盟
40 exit 0xJDLinux联盟
################################End Script#########################################xJDLinux联盟
  注意: 与()中的命令不同的是,{}中的代码块将不能正常地开启一个新shell.[2]xJDLinux联盟
{} \; 路径名.一般都在find命令中使用.这不是一个shell内建命令.xJDLinux联盟
  注意: ";"用来结束find命令序列的-exec选项.xJDLinux联盟
[]  test.xJDLinux联盟
  test的表达式将在[]中.xJDLinux联盟
  值得注意的是[是shell内建test命令的一部分,并不是/usr/bin/test中的扩展命令xJDLinux联盟
  的一个连接.xJDLinux联盟
[[]] test.xJDLinux联盟
  test表达式放在[[]]中.(shell关键字)xJDLinux联盟
  具体查看[[]]结构的讨论.xJDLinux联盟
[]  数组元素xJDLinux联盟
  Array[1]=slot_1xJDLinux联盟
  echo ${Array[1]}xJDLinux联盟
[]  字符范围xJDLinux联盟
  在正则表达式中使用,作为字符匹配的一个范围xJDLinux联盟
(()) 数学计算的扩展xJDLinux联盟
  在(())结构中可以使用一些数字计算.xJDLinux联盟
  具体参阅((...))结构.xJDLinux联盟
>&>>&>><xJDLinux联盟
  重定向.xJDLinux联盟
  scriptname >filename 重定向脚本的输出到文件中.覆盖文件原有内容.xJDLinux联盟
  command &>filename 重定向stdout和stderr到文件中xJDLinux联盟
  command >&2 重定向command的stdout到stderrxJDLinux联盟
  scriptname >>filename 重定向脚本的输出到文件中.添加到文件尾端,如果没有文件,xJDLinux联盟
  则创建这个文件.xJDLinux联盟
  进程替换,具体见"进程替换部分",跟命令替换极其类似.xJDLinux联盟
  (command)>xJDLinux联盟
  <(command)xJDLinux联盟
  <和> 可用来做字符串比较xJDLinux联盟
  <和> 可用在数学计算比较xJDLinux联盟
<<  重定向,用在"here document"xJDLinux联盟
<<<  重定向,用在"here string"xJDLinux联盟
<,>  ASCII比较xJDLinux联盟
   1 veg1=carrotsxJDLinux联盟
   2 veg2=tomatoesxJDLinux联盟
   3 xJDLinux联盟
   4 if [[ "$veg1" < "$veg2" ]]xJDLinux联盟
   5 thenxJDLinux联盟
   6   echo "Although $veg1 precede $veg2 in the dictionary,"xJDLinux联盟
   7   echo "this implies nothing about my culinary preferences."xJDLinux联盟
   8 elsexJDLinux联盟
   9   echo "What kind of dictionary are you using, anyhow?"xJDLinux联盟
  10 fixJDLinux联盟
\<,\> 正则表达式中的单词边界.如:xJDLinux联盟
  bash$grep '\<the\>' textfilexJDLinux联盟
 xJDLinux联盟
|  管道.分析前边命令的输出,并将输出作为后边命令的输入.这是一种产生命令链的xJDLinux联盟
  好方法.xJDLinux联盟
  1 echo ls -l | shxJDLinux联盟
  2 #  传递"echo ls -l"的输出到shell中,xJDLinux联盟
  3 #+ 与一个简单的"ls -l"结果相同.xJDLinux联盟
  4 xJDLinux联盟
  5 xJDLinux联盟
  6 cat *.lst | sort | uniqxJDLinux联盟
  7 # 合并和排序所有的".lst"文件,然后删除所有重复的行.xJDLinux联盟
  xJDLinux联盟
  管道是进程间通讯的一个典型办法,将一个进程的stdout放到另一个进程的stdin中.xJDLinux联盟
  标准的方法是将一个一般命令的输出,比如cat或echo,传递到一个过滤命令中(在这个xJDLinux联盟
  过滤命令中将处理输入),得到结果,如:xJDLinux联盟
  cat $filename1 | $filename2 | grep $search_wordxJDLinux联盟
  当然输出的命令也可以传递到脚本中.如:xJDLinux联盟
################################Start Script#######################################xJDLinux联盟
1 #!/bin/bashxJDLinux联盟
2 # uppercase.sh : 修改输出,全部转换为大写xJDLinux联盟
3 xJDLinux联盟
4 tr 'a-z' 'A-Z'xJDLinux联盟
5 #  字符范围必须被""引用起来xJDLinux联盟
6 #+ 来阻止产生单字符的文件名.xJDLinux联盟
7 xJDLinux联盟
8 exit 0xJDLinux联盟
################################End Script#########################################xJDLinux联盟
  xJDLinux联盟
  现在让我们输送ls -l的输出到一个脚本中.xJDLinux联盟
  bash$ ls -l | ./uppercase.shxJDLinux联盟
  -RW-RW-R--    1 BOZO  BOZO       109 APR  7 19:49 1.TXTxJDLinux联盟
  -RW-RW-R--    1 BOZO  BOZO       109 APR 14 16:48 2.TXTxJDLinux联盟
  -RW-R--R--    1 BOZO  BOZO       725 APR 20 20:56 DATA-FILExJDLinux联盟
  注意:管道中的一个进程的stdout必须被下一个进程作为stdin读入.否则,数据流会阻xJDLinux联盟
  塞,并且管道将产生非预期的行为.xJDLinux联盟
  如:xJDLinux联盟
  1 cat file1 file2 | ls -l | sortxJDLinux联盟
  2 #从"cat file1 file2"中的输出并没出现xJDLinux联盟
  作为子进程的运行的管道,不能够改变脚本的变量.xJDLinux联盟
  1 variable="initial_value"xJDLinux联盟
  2 echo "new_value" | read variablexJDLinux联盟
  3 echo "variable = $variable"   #variable = initial_valuexJDLinux联盟
  如果管道中的某个命令产生了一个异常,并中途失败,那么这个管道将过早的终止.xJDLinux联盟
  这种行为被叫做a broken pipe,并且这种状态下将发送一个SIGPIPE信号.xJDLinux联盟
>|  强制重定向(即使设置了noclobber选项--就是-C选项).这将强制的覆盖一个现存文件.xJDLinux联盟
||  或-逻辑操作.xJDLinux联盟
&  后台运行命令.一个命令后边跟一个&,将表示在后台运行.xJDLinux联盟
  bash$sleep 10 &xJDLinux联盟
  [1] 850xJDLinux联盟
  [1]+ Done   sleep 10xJDLinux联盟
  在一个脚本中,命令和循环都可能运行在后台.xJDLinux联盟
Example 3-3. 在后台运行一个循环xJDLinux联盟
################################Start Script#######################################xJDLinux联盟
 1 #!/bin/bashxJDLinux联盟
 2 #background-loop.shxJDLinux联盟
 3xJDLinux联盟
 4 for i in 1 2 3 4 5 6 7 8 9 10    #第一个循环xJDLinux联盟
 5 doxJDLinux联盟
 6 echo -n "$i"xJDLinux联盟
 7 done&      #在后台运行这个循环xJDLinux联盟
 8        #在第2个循环之后,将在某些时候执行.xJDLinux联盟
 9 xJDLinux联盟
10 echo #这个'echo'某些时候将不会显示.xJDLinux联盟
11 xJDLinux联盟
12 for i in 11 12 13 14 15 16 17 18 19 20  #第二个循环xJDLinux联盟
13 doxJDLinux联盟
14  echo -n "$i"xJDLinux联盟
15 donexJDLinux联盟
16xJDLinux联盟
17 echo #这个'echo'某些时候将不会显示.xJDLinux联盟
18xJDLinux联盟
19 #--------------------------------------------------------xJDLinux联盟
20 xJDLinux联盟
21 #期望的输出应该是xJDLinux联盟
22 #1 2 3 4 5 6 7 8 9 10xJDLinux联盟
23 #11 12 13 14 15 16 17 18 19 20xJDLinux联盟
24 xJDLinux联盟
25 #然而实际的结果有可能是xJDLinux联盟
26 #11 12 13 14 15 16 17 18 19 20xJDLinux联盟
27 #1 2 3 4 5 6 7 8 9 10 bozo $xJDLinux联盟
28 #(第2个'echo'没执行,为什么?)xJDLinux联盟
29 xJDLinux联盟
30 #也可能是xJDLinux联盟
31 #1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20xJDLinux联盟
32 #(第1个'echo'没执行,为什么?)xJDLinux联盟
33 xJDLinux联盟
34 #非常少见的执行结果,也有可能是:xJDLinux联盟
35 #11 12 13 1 2 3 4 5 6 7 8 9 10 14 15 16 17 18 19 20xJDLinux联盟
36 #前台的循环先于后台的执行xJDLinux联盟
37 xJDLinux联盟
38 exit 0xJDLinux联盟
39 xJDLinux联盟
40 # Nasimuddin Ansari 建议加一句 sleep 1xJDLinux联盟
41 #+ 在 6行和14行的 echo -n "$i"之后加xJDLinux联盟
42 #+ 将看到一些乐趣xJDLinux联盟
################################End Script#########################################xJDLinux联盟
  注意:在一个脚本内后台运行一个命令,有可能造成这个脚本的挂起,等待一个按键xJDLinux联盟
   响应.幸运的是,我们可以在Example 11-24附近,看到这个问题的解决办法.xJDLinux联盟
&&  与-逻辑操作.xJDLinux联盟
-  选项,前缀.在所有的命令内如果想使用选项参数的话,前边都要加上"-".xJDLinux联盟
  COMMAND -[Option1][Option2][...]xJDLinux联盟
  ls -alxJDLinux联盟
  sort -dfu $filenamexJDLinux联盟
  set -- $variablexJDLinux联盟
   1 if [ $file1 -ot $file2 ]xJDLinux联盟
   2 thenxJDLinux联盟
   3   echo "File $file1 is older than $file2."xJDLinux联盟
   4 fixJDLinux联盟
   5 xJDLinux联盟
   6 if [ "$a" -eq "$b" ]xJDLinux联盟
   7 thenxJDLinux联盟
   8   echo "$a is equal to $b."xJDLinux联盟
   9 fixJDLinux联盟
  10 xJDLinux联盟
  11 if [ "$c" -eq 24 -a "$d" -eq 47 ]xJDLinux联盟
  12 thenxJDLinux联盟
  13   echo "$c equals 24 and $d equals 47."xJDLinux联盟
  14 fixJDLinux联盟
-  用于重定向 stdin 或 stdout.xJDLinux联盟
################################Start Script#######################################xJDLinux联盟
 1 (cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)xJDLinux联盟
 2 # 从一个目录移动整个目录树到另一个目录xJDLinux联盟
 3 # [courtesy Alan Cox <a.cox@swansea.ac.uk>, with a minor change]xJDLinux联盟
 4 xJDLinux联盟
 5 # 1) cd /source/directory    源目录xJDLinux联盟
 6 # 2) &&                      与操作,如果cd命令成功了,那么就执行下边的命令xJDLinux联盟
 7 # 3) tar cf - .              'c'创建一个新文档,'f'后边跟'-'指定目标文件作为stdoutxJDLinux联盟
 8 #                            '-'后边的'f'(file)选项,指明作为stdout的目标文件.xJDLinux联盟
 9 #                            并且在当前目录('.')执行.xJDLinux联盟
10 # 4) |                       管道...xJDLinux联盟
11 # 5) ( ... )                 一个子shellxJDLinux联盟
12 # 6) cd /dest/directory      改变当前目录到目标目录.xJDLinux联盟
13 # 7) &&                      与操作,同上.xJDLinux联盟
14 # 8) tar xpvf -              'x'解档,'p'保证所有权和文件属性,xJDLinux联盟
15 #       'v'发完整消息到stdoutxJDLinux联盟
16 #                            'f'后边跟'-',从stdin读取数据xJDLinux联盟
17 #xJDLinux联盟
18 #                            注意:'x' 是一个命令, 'p', 'v', 'f' 是选项.xJDLinux联盟
19 # Whew!xJDLinux联盟
20 xJDLinux联盟
21 xJDLinux联盟
22 xJDLinux联盟
23 # 更优雅的写法应该是xJDLinux联盟
24 #   cd source/directoryxJDLinux联盟
25 #   tar cf - . | (cd ../dest/directory; tar xpvf -)xJDLinux联盟
26 #xJDLinux联盟
27 #     当然也可以这么写:xJDLinux联盟
28 # cp -a /source/directory/* /dest/directoryxJDLinux联盟
29 #     或者:xJDLinux联盟
30 # cp -a /source/directory/* /source/directory/.[^.]* /dest/directoryxJDLinux联盟
31 #     如果在/source/directory中有隐藏文件的话.xJDLinux联盟
################################End Script#########################################xJDLinux联盟
################################Start Script#######################################xJDLinux联盟
1 bunzip2 linux-2.6.13.tar.bz2 | tar xvf -xJDLinux联盟
2 # --未解压的tar文件--    | --然后把它传递到"tar"中--xJDLinux联盟
3 # 如果 "tar" 没能够正常的处理"bunzip2",xJDLinux联盟
4 # 这就需要使用管道来执行2个单独的步骤来完成它.xJDLinux联盟
5 # 这个练习的目的是解档"bzipped"的kernel源文件.xJDLinux联盟
################################End Script#########################################xJDLinux联盟
  注意:在上边这个例子中'-'不太象是bash的操作符,而更像是tar的参数.xJDLinux联盟
  bash$echo "whatever" | cat -xJDLinux联盟
  whateverxJDLinux联盟
  在需要一个文件名的地方,-重定向输出到stdout(如在tar和cf命令中),或者从xJDLinux联盟
  stdin中接受输入,而不是从一个文件中接受输入.这是在管道中作为一个过滤xJDLinux联盟
  器,来使用文件定位工具的一种办法.xJDLinux联盟
  bash$filexJDLinux联盟
  用法: file [-bciknvzl] [-f namefile] [-m magicfiles] file...xJDLinux联盟
  上边这个例子file将会出错,提示你如何使用file命令.xJDLinux联盟
  添加一个"-"将得到一个更有用的结果.这将使得shell等待用户输入.xJDLinux联盟
  bash$file -xJDLinux联盟
  abcxJDLinux联盟
  standard input:     ASCII textxJDLinux联盟
  bash$file -xJDLinux联盟
  #!/bin/bashxJDLinux联盟
  standard input:     Bourn-Again shell script tesxt executablexJDLinux联盟
  现在命令从stdin中接受了输入,并分析它.xJDLinux联盟
  "-"常用于管道后边的命令,具体参看33.7节,来看使用技巧.xJDLinux联盟
  使用diff命令来和另一个文件的一部分进行比较.xJDLinux联盟
  grep Linux file1 | diff file2 -xJDLinux联盟
  最后,一个真实世界的使用tar命令的例子.xJDLinux联盟
Example 3-4. 备份最后一天所有修改的文件.xJDLinux联盟
################################Start Script#######################################xJDLinux联盟
 1 #!/bin/bashxJDLinux联盟
 2 xJDLinux联盟
 3 #  在一个"tarball"中(经过tar和gzip处理过的文件)xJDLinux联盟
 4 #+ 备份最后24小时当前目录下d所有修改的文件.xJDLinux联盟
 5 xJDLinux联盟
 6 BACKUPFILE=backup-$(date +%m-%d-%Y)xJDLinux联盟
 7 #                 在备份文件中嵌入时间.xJDLinux联盟
 8 #                 Thanks, Joshua Tschida, for the idea.xJDLinux联盟
 9 archive=${1:-$BACKUPFILE}xJDLinux联盟
10 #  如果在命令行中没有指定备份文件的文件名,xJDLinux联盟
11 #+ 那么将默认使用"backup-MM-DD-YYYY.tar.gz".xJDLinux联盟
12 xJDLinux联盟
13 tar cvf - `find . -mtime -1 -type f -print` > $archive.tarxJDLinux联盟
14 gzip $archive.tarxJDLinux联盟
15 echo "Directory $PWD backed up in archive file \"$archive.tar.gz\"."xJDLinux联盟
16 xJDLinux联盟
17 xJDLinux联盟
18 #  Stephane Chazelas指出上边代码,xJDLinux联盟
19 #+ 如果在发现太多的文件的时候,或者是如果文件xJDLinux联盟
20 #+ 名包括空格的时候,将执行失败.xJDLinux联盟
21 xJDLinux联盟
22 # Stephane Chazelas建议使用下边的两种代码之一xJDLinux联盟
23 # -------------------------------------------------------------------xJDLinux联盟
24 #   find . -mtime -1 -type f -print0 | xargs -0 tar rvf "$archive.tar"xJDLinux联盟
25 #      使用gnu版本的find.xJDLinux联盟
26 xJDLinux联盟
27 xJDLinux联盟
28 #   find . -mtime -1 -type f -exec tar rvf "$archive.tar" '{}' \;xJDLinux联盟
29 #         对于其他风格的UNIX便于移植,但是比较慢.xJDLinux联盟
30 # -------------------------------------------------------------------xJDLinux联盟
31 xJDLinux联盟
32 xJDLinux联盟
33 exit 0xJDLinux联盟
################################End Script#########################################xJDLinux联盟
  注意:以"-"开头的文件名在使用"-"作为重定向操作符的时候,可能会产生问题.xJDLinux联盟
  应该写一个脚本来检查这个问题,并给这个文件加上合适的前缀.如:xJDLinux联盟
  ./-FILENAME, $PWD/-FILENAME,或$PATHNAME/-FILENAME.xJDLinux联盟
  如果变量的值以"-"开头,可能也会引起问题.xJDLinux联盟
  1 var="-n"xJDLinux联盟
  2 echo $varxJDLinux联盟
  3 #具有"echo -n"的效果了,这样什么都不会输出的.xJDLinux联盟
-  之前工作的目录."cd -"将回到之前的工作目录,具体请参考"$OLDPWD"环境变量.xJDLinux联盟
  注意:一定要和之前讨论的重定向功能分开,但是只能依赖上下文区分.xJDLinux联盟
-  算术减号.xJDLinux联盟
=  算术等号,有时也用来比较字符串.xJDLinux联盟
  1 a=28xJDLinux联盟
  2 echo $a   # 28xJDLinux联盟
+  算术加号,也用在正则表达式中.xJDLinux联盟
+  选项,对于特定的命令来说使用"+"来打开特定的选项,用"-"来关闭特定的选项.xJDLinux联盟
%  算术取模运算.也用在正则表达式中.xJDLinux联盟
~  home目录.相当于$HOME变量.~bozo是bozo的home目录,并且ls ~bozo将列出其中的xJDLinux联盟
  内容. ~/就是当前用户的home目录,并且ls ~/将列出其中的内容,如:xJDLinux联盟
  bash$ echo ~bozoxJDLinux联盟
  /home/bozoxJDLinux联盟
  bash$ echo ~xJDLinux联盟
  /home/bozoxJDLinux联盟
  bash$ echo ~/xJDLinux联盟
  /home/bozo/xJDLinux联盟
  bash$ echo ~:xJDLinux联盟
  /home/bozo:xJDLinux联盟
  bash$ echo ~nonexistent-userxJDLinux联盟
  ~nonexistent-userxJDLinux联盟
~+  当前工作目录,相当于$PWD变量.xJDLinux联盟
~-  之前的工作目录,相当于$OLDPWD内部变量.xJDLinux联盟
=~  用于正则表达式,这个操作将在正则表达式匹配部分讲解,只有version3才支持.xJDLinux联盟
^  行首,正则表达式中表示行首."^"定位到行首.

控制字符xJDLinux联盟
  修改终端或文本显示的行为.控制字符以CONTROL + key组合.xJDLinux联盟
  控制字符在脚本中不能正常使用.xJDLinux联盟
  Ctl-B  光标后退,这应该依赖于bash输入的风格,默认是emacs风格的.xJDLinux联盟
  Ctl-C  Break,终止前台工作.xJDLinux联盟
  Ctl-D  从当前shell登出(和exit很像)xJDLinux联盟
     "EOF"(文件结束符).这也能从stdin中终止输入.xJDLinux联盟
     在console或者在xterm window中输入的时候,Ctl-D将删除光标下字符.xJDLinux联盟
     当没有字符时,Ctrl-D将退出当前会话.在xterm window也有关闭窗口xJDLinux联盟
     的效果.xJDLinux联盟
  Ctl-G  beep.在一些老的终端,将响铃.xJDLinux联盟
  Ctl-H  backspace,删除光标前边的字符.如:xJDLinux联盟
      1 #!/bin/bashxJDLinux联盟
      2 # 在一个变量中插入Ctl-HxJDLinux联盟
      3 xJDLinux联盟
      4 a="^H^H"                  # 两个 Ctl-H (backspaces).xJDLinux联盟
      5 echo "abcdef"             # abcdefxJDLinux联盟
      6 echo -n "abcdef$a "       # abcd fxJDLinux联盟
      7 # 注意结尾的空格 ^              ^ 两个 twice.xJDLinux联盟
      8 echo -n "abcdef$a"        # abcdefxJDLinux联盟
      9 #  结尾没有空格             没有 backspace 的效果了(why?).xJDLinux联盟
     10                           # 结果并不像期望的那样xJDLinux联盟
     11 echo; echoxJDLinux联盟
  Ctl-I  就是tab键.xJDLinux联盟
  Ctl-J  新行.xJDLinux联盟
  Ctl-K  垂直tab.(垂直tab?新颖,没听过)xJDLinux联盟
     作用就是删除光标到行尾的字符.xJDLinux联盟
  Ctl-L  clear,清屏.xJDLinux联盟
  Ctl-M  回车xJDLinux联盟
################################Start Script#######################################xJDLinux联盟
 1 #!/bin/bashxJDLinux联盟
 2 # Thank you, Lee Maschmeyer, for this example.xJDLinux联盟
 3 xJDLinux联盟
 4 read -n 1 -s -p $'Control-M leaves cursor at beginning of this line. Press Enter. \x0d'xJDLinux联盟
 5                                   #当然,'0d'就是二进制的回车.xJDLinux联盟
 6 echo >&2   #  '-s'参数使得任何输入都不将回显出来xJDLinux联盟
 7            #+ 所以,明确的重起一行是必要的.xJDLinux联盟
 8 xJDLinux联盟
 9 read -n 1 -s -p $'Control-J leaves cursor on next line. \x0a'xJDLinux联盟
10 echo >&2   #  Control-J 是换行.xJDLinux联盟
11 xJDLinux联盟
12 ###xJDLinux联盟
13 xJDLinux联盟
14 read -n 1 -s -p $'And Control-K\x0bgoes straight down.'xJDLinux联盟
15 echo >&2   #  Control-K 是垂直制表符.xJDLinux联盟
16 xJDLinux联盟
17 # 关于垂直制表符效果的一个更好的例子见下边:xJDLinux联盟
18 xJDLinux联盟
19 var=$'\x0aThis is the bottom line\x0bThis is the top line\x0a'xJDLinux联盟
20 echo "$var"xJDLinux联盟
21 #  这句与上边的例子使用的是同样的办法,然而:xJDLinux联盟
22 echo "$var" | colxJDLinux联盟
23 #  这将造成垂直制表符右边的部分在左边部分的上边.xJDLinux联盟
24 #  这也解释了为什么我们要在行首和行尾加上一个换行符--xJDLinux联盟
25 #+ 来避免一个混乱的屏幕输出.xJDLinux联盟
26 xJDLinux联盟
27 # Lee Maschmeyer的解释:xJDLinux联盟
28 # ---------------------xJDLinux联盟
29 #  In the [first vertical tab example] . . . the vertical tabxJDLinux联盟
29 #  在这里[第一个垂直制表符的例子中] . . . 这个垂直制表符xJDLinux联盟
30 #+ makes the printing go straight down without a carriage return.xJDLinux联盟
31 #  This is true only on devices, such as the Linux console,xJDLinux联盟
32 #+ that can't go "backward."xJDLinux联盟
33 #  The real purpose of VT is to go straight UP, not down.xJDLinux联盟
34 #  It can be used to print superscripts on a printer.xJDLinux联盟
34 #  它可以用来在一个打印机上打印上标.xJDLinux联盟
35 #  col的作用,可以用来模仿VT的合适的行为.xJDLinux联盟
36 xJDLinux联盟
37 exit 0xJDLinux联盟
################################End Script#########################################xJDLinux联盟
  Ctl-Q  继续(等价于XON字符),这个继续的标准输入在一个终端里xJDLinux联盟
  Ctl-S  挂起(等价于XOFF字符),这个被挂起的stdin在一个终端里,用Ctl-Q恢复xJDLinux联盟
  Ctl-U  删除光标到行首的所有字符,在某些设置下,删除全行.xJDLinux联盟
  Ctl-V  当输入字符时,Ctl-V允许插入控制字符.比如,下边2个例子是等价的xJDLinux联盟
     echo -e '\x0a'xJDLinux联盟
     echo <Ctl-V><Ctl-J>xJDLinux联盟
     Ctl-V在文本编辑器中十分有用,在vim中一样.xJDLinux联盟
  Ctl-W  删除当前光标到前边的最近一个空格之间的字符.xJDLinux联盟
     在某些设置下,删除到第一个非字母或数字的字符.xJDLinux联盟
  Ctl-Z  终止前台工作.xJDLinux联盟
  xJDLinux联盟
空白部分xJDLinux联盟
  分割命令或者是变量.包括空格,tab,空行,或任何它们的组合.xJDLinux联盟
  在一些特殊情况下,空白是不允许的,如变量赋值时,会引起语法错误.xJDLinux联盟
  空白行在脚本中没有效果.xJDLinux联盟
  "$IFS",对于某些命令输入的特殊变量分割域,默认使用的是空白.xJDLinux联盟
  如果想保留空白,使用引用.xJDLinux联盟
注意事项:xJDLinux联盟
[1]  shell做大括号的命令扩展.但是命令本身需要对扩展的结果作处理.xJDLinux联盟
[2]  例外:在pipe中的一个大括号中的代码段可能运行在一个子shell中.xJDLinux联盟
  1 ls | { read firstline; read secondline; }xJDLinux联盟
  2 #  错误,在打括号中的代码段,将运行到子shell中.xJDLinux联盟
  3 #+ 所以ls的输出将不能传递到代码块中.xJDLinux联盟
  4 echo "First line is $firstline; second line is $secondline"  # 不能工作xJDLinux联盟
  5 xJDLinux联盟
  6 # Thanks, S.C.xJDLinux联盟
[3]  换行符也被认为是空白.这也解释了为什么一个空行也会被认为是空白.xJDLinux联盟
 xJDLinux联盟
第4章 变量和参数的介绍xJDLinux联盟
======================xJDLinux联盟
4.1 变量替换xJDLinux联盟
------------xJDLinux联盟
$  变量替换操作符xJDLinux联盟
  只有在变量被声明,赋值,unset或exported或者是在变量代表一个signal的时候,xJDLinux联盟
  变量才会是以本来的面目出现在脚本里.变量在被赋值的时候,可能需要使用"=",xJDLinux联盟
  read状态或者是在循环的头部.xJDLinux联盟
  在""中还是会发生变量替换,这被叫做部分引用,或叫弱引用.而在''中就不会发生变xJDLinux联盟
  量替换,这叫做全引用,也叫强引用.具体见第5章的讨论.xJDLinux联盟
  注意:$var与${var}的区别,不加{},在某些上下文将引起错误,为了安全,使用2.xJDLinux联盟
   具体见9.3节 参数替换.xJDLinux联盟
Example 4-1. 变量赋值和替换xJDLinux联盟
################################Start Script#######################################xJDLinux联盟
 1 #!/bin/bashxJDLinux联盟
 2 xJDLinux联盟
 3 # 变量赋值和替换xJDLinux联盟
 4 xJDLinux联盟
 5 a=375xJDLinux联盟
 6 hello=$axJDLinux联盟
 7 xJDLinux联盟
 8 #-------------------------------------------------------------------------xJDLinux联盟
 9 # 强烈注意,在赋值的前后一定不要有空格.xJDLinux联盟
10 # 如果有空格会发生什么?xJDLinux联盟
11 xJDLinux联盟
12 #  如果"VARIABLE =value",xJDLinux联盟
13 #              ^xJDLinux联盟
14 #+ 脚本将尝试运行一个"VARIABLE"的命令,带着一个"=value"参数.xJDLinux联盟
15 xJDLinux联盟
16 #  如果"VARIABLE= value",xJDLinux联盟
17 #               ^xJDLinux联盟
18 #+ script tries to run "value" command withxJDLinux联盟
18 #+ 脚本将尝试运行一个"value"的命令,带着xJDLinux联盟
19 #+ the environmental variable "VARIABLE" set to "".xJDLinux联盟
19 #+ 一个被赋成""值的环境变量"VARIABLE".xJDLinux联盟
20 #-------------------------------------------------------------------------xJDLinux联盟
21 xJDLinux联盟
22 xJDLinux联盟
23 echo hello    # 没有变量引用,不过是个hello字符串xJDLinux联盟
24 xJDLinux联盟
25 echo $helloxJDLinux联盟
26 echo ${hello} # 同上xJDLinux联盟
27 xJDLinux联盟
28 echo "$hello"xJDLinux联盟
29 echo "${hello}"xJDLinux联盟
30 xJDLinux联盟
31 echoxJDLinux联盟
32 xJDLinux联盟
33 hello="A B  C   D"xJDLinux联盟
34 echo $hello   # A B C DxJDLinux联盟
35 echo "$hello" # A B  C   DxJDLinux联盟
36 # 就象你看到的echo $hello   和    echo "$hello"   将给出不同的结果.xJDLinux联盟
37 #                                      ^      ^xJDLinux联盟
38 # Quoting a variable preserves whitespace.xJDLinux联盟
38 # 引用一个变量将保留其中的空白,当然,如果是变量替换就不会保留了.xJDLinux联盟
39 xJDLinux联盟
40 echoxJDLinux联盟
41 xJDLinux联盟
42 echo '$hello'  # $helloxJDLinux联盟
43 #    ^      ^xJDLinux联盟
44 # 全引用的作用xJDLinux联盟
45 #+ 将导致"$"变成一个单独的字符.xJDLinux联盟
46 xJDLinux联盟
47 # 注意两种引用不同的效果xJDLinux联盟
48 xJDLinux联盟
49 xJDLinux联盟
50 hello=    # 设置为空值xJDLinux联盟
51 echo "\$hello (null value) = $hello"xJDLinux联盟
52 #  注意设置一个变量为空,与unset它,不是一回事,虽然看起来一样xJDLinux联盟
53 #xJDLinux联盟
54 xJDLinux联盟
55 # --------------------------------------------------------------xJDLinux联盟
56 xJDLinux联盟
57 #  可以在同一行上设置多个变量.xJDLinux联盟
58 #+ 要以空白分隔xJDLinux联盟
59 #  小心,这会降低可读性,和可移植性.xJDLinux联盟
60 xJDLinux联盟
61 var1=21