文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>高级shell编程指南2

高级shell编程指南2

时间:2006-06-01  来源:lin_lin13

<meta http-equiv="CONTENT-TYPE" content="text/html; charset=utf-8"><title></title><meta name="GENERATOR" content="OpenOffice.org 2.0 (Linux)"><meta name="AUTHOR" content="lin lin"><meta name="CREATED" content="20060525;8284000"><meta name="CHANGEDBY" content="lin lin"><meta name="CHANGED" content="20060525;8323700"> <style> </style>

第一部分 热身

++++++++++++++++

shell是一个命令解释器.是介于操作系统kernel与用户之间的一个绝缘层.准确地说,它也是一

一种强力的计算机语言.一个shell程序,被称为一个脚本,是一种很容易使用的工具,它可以通过

将系统调用,公共程序,工具,和编译过的二进制程序粘合在一起来建立应用.事实上,所有的UNIX

命令和工具再加上公共程序,对于shell脚本来说,都是可调用的.如果这些你还觉得不够,那么

shell内建命令,比如test与循环结构,也会给脚本添加强力的支持和增加灵活性.Shell脚本对于

管理系统任务和其它的重复工作的例程来说,表现的非常好,根本不需要那些华而不实的成熟

紧凑的程序语言.


第1章 为什么使用shell编程

===========================

没有程序语言是完美的.甚至没有一个唯一最好的语言,只有对于特定目的,比较适合和不适合

的程序语言.

Herbert Mayer


对于任何想适当精通一些系统管理知识的人来说,掌握shell脚本知识都是最基本的,即使这些

人可能并不打算真正的编写一些脚本.想一下Linux机器的启动过程,在这个过程中,必将运行

/etc/rc.d目录下的脚本来存储系统配置和建立服务.详细的理解这些启动脚本对于分析系统的

行为是非常重要的,并且有时候可能必须修改它.


学习如何编写shell脚本并不是一件很困难的事,因为脚本可以分为很小的块,并且相对于shell

特性的操作和选项[1]部分,只需要学习很小的一部分就可以了.语法是简单并且直观的,编写脚

本很像是在命令行上把一些相关命令和工具连接起来,并且只有很少的一部分规则需要学习.

绝大部分脚本第一次就可以正常的工作,而且即使调试一个长一些的脚本也是很直观的.


一个shell脚本是一个类似于小吃店的(quick and dirty)方法,在你使用原型设计一个复杂的

应用的时候.在工程开发的第一阶段,即使从功能中取得很有限的一个子集放到shell脚本中来

完成往往都是非常有用的.使用这种方法,程序的结果可以被测试和尝试运行,并且在处理使用

诸如C/C++,Java或者Perl语言编写的最终代码前,主要的缺陷和陷阱往往就被发现了.


Shell脚本遵循典型的UNIX哲学,就是把大的复杂的工程分成小规模的子任务,并且把这些部件

和工具组合起来.许多人认为这种办法更好一些,至少这种办法比使用那种高\大\全的语言更

美,更愉悦,更适合解决问题.比如Perl就是这种能干任何事能适合任何人的语言,但是代价就是

你需要强迫自己使用这种语言来思考解决问题的办法.


什么时候不使用Shell脚本

资源密集型的任务,尤其在需要考虑效率时(比如,排序,hash等等)


需要处理大任务的数学操作,尤其是浮点运算,精确运算,或者复杂的算术运算

(这种情况一般使用C++或FORTRAN来处理)


有跨平台移植需求(一般使用C或Java)


复杂的应用,在必须使用结构化编程的时候(需要变量的类型检查,函数原型,等等)


对于影响系统全局性的关键任务应用。


对于安全有很高要求的任务,比如你需要一个健壮的系统来防止入侵,破解,恶意破坏等等.


项目由连串的依赖的各个部分组成。


需要大规模的文件操作


需要多维数组的支持


需要数据结构的支持,比如链表或数等数据结构


需要产生或操作图形化界面GUI


需要直接操作系统硬件


需要I/O或socket接口


需要使用库或者遗留下来的老代码的接口


私人的,闭源的应用(shell脚本把代码就放在文本文件中,全世界都能看到)


如果你的应用符合上边的任意一条,那么就考虑一下更强大的语言吧--或许是Perl,Tcl,Python,

Ruby -- 或者是更高层次的编译语言比如C/C++,或者是Java.即使如此,你会发现,使用shell

来原型开发你的应用,在开发步骤中也是非常有用的.


我们将开始使用Bash,Bash是"Bourne-Again shell"首字母的缩写,也是Stephen Bourne的经典

的Bourne shell的一个双关语,(译者:说实话,我一直搞不清这个双关语是什么意思,为什么叫

"Bourn-Again shell",这其中应该有个什么典故吧,哪位好心,告诉我一下^^).Bash已经成为了

所有UNIX中shell脚本的事实上的标准了.同时这本书也覆盖了绝大部分的其他一些shell的原

则,比如Korn Shell,Bash从ksh中继承了一部分特性,[2]C Shell和它的变种.(注意:C Shell

编程是不被推荐的,因为一些特定的内在问题,Tom Christiansen在1993年10月指出了这个问题

请在http://www.etext.org/Quartz/computer/unix/csh.harmful.gz中查看具体内容.)


接下来是脚本的一些说明.在展示shell不同的特征之前,它可以减轻一些阅读书中例子

的负担.本书中的例子脚本,都在尽可能的范围内进行了测试,并且其中的一些将使用在真

实的生活中.读者可以运行这些例子脚本(使用scriptname.sh或者scriptname.bash的形式),

[3]并给这些脚本执行权限(chmod u+rx scriptname),然后执行它们,看看发生了什么.如果存

档的脚本不可用,那么就从本书的HTML,pdf或者text的发行版本中把它们拷贝粘贴出来.考虑到

这些脚本中的内容在我们还没解释它之前就被列在这里,可能会影响读者的理解,这就需要读者

暂时忽略这些内容.


除非特别注明,本书作者编写了本书中的绝大部分例子脚本.


注意事项:

[1] 这些在builtins章节被引用,这些是shell的内部特征.

[2] ksh88的许多特性,甚至是一些ksh93的特性都被合并到Bash中了.

[3] 根据惯例,用户编写的Bourne shell脚本应该在脚本的名字后边加上.sh扩展名.

一些系统脚本,比如那些在/etc/rc.d中的脚本,则不遵循这种命名习惯.




第2章 带着一个Sha-Bang出发(Sha-Bang指的是#!)

==============================================

在一个最简单的例子中,一个shell脚本其实就是将一堆系统命令列在一个文件中.它的最基本的

用处就是,在你每次输入这些特定顺序的命令时可以少敲一些字.


Example 2-1 清除:清除/var/log下的log文件

################################Start Script#######################################

1 # Cleanup

2 # 当然要使用root身份来运行这个脚本

3

4 cd /var/log

5 cat /dev/null > messages

6 cat /dev/null > wtmp

7 echo "Logs cleaned up."

################################End Script#########################################

这根本就没什么稀奇的, 只不过是命令的堆积, 来让从console或者xterm中一个一个的输入命

令更方便一些.好处就是把所有命令都放在一个脚本中,不用每次都敲它们.这样的话,对于特定

的应用来说,这个脚本就很容易被修改或定制.


Example 2-2 清除:一个改良的清除脚本

################################Start Script#######################################

1 #!/bin/bash

2 # 一个Bash脚本的正确的开头部分.

3

4 # Cleanup, 版本 2

5

6 # 当然要使用root身份来运行.

7 # 在此处插入代码,来打印错误消息,并且在不是root身份的时候退出.

8

9 LOG_DIR=/var/log

10 # 如果使用变量,当然比把代码写死的好.

11 cd $LOG_DIR

12

13 cat /dev/null > messages

14 cat /dev/null > wtmp

15

16

17 echo "Logs cleaned up."

18

19 exit # 这个命令是一种正确并且合适的退出脚本的方法.

################################End Script#########################################


现在,让我们看一下一个真正意义的脚本.而且我们可以走得更远...

Example 2-3. cleanup:一个增强的和广义的删除logfile的脚本

################################Start Script#######################################

1 #!/bin/bash

2 # 清除, 版本 3

3

4 # Warning:

5 # -------

6 # 这个脚本有好多特征,这些特征是在后边章节进行解释的,大概是进行到本书的一半的

7 # 时候,

8 # 你就会觉得它没有什么神秘的了.

9 #

10

11

12

13 LOG_DIR=/var/log

14 ROOT_UID=0 # $UID为0的时候,用户才具有根用户的权限

15 LINES=50 # 默认的保存行数

16 E_XCD=66 # 不能修改目录?

17 E_NOTROOT=67 # 非根用户将以error退出

18

19

20 # 当然要使用根用户来运行

21 if [ "$UID" -ne "$ROOT_UID" ]

22 then

23 echo "Must be root to run this script."

24 exit $E_NOTROOT

25 fi

26

27 if [ -n "$1" ]

28 # 测试是否有命令行参数(非空).

29 then

30 lines=$1

31 else

32 lines=$LINES # 默认,如果不在命令行中指定

33 fi

34

35

36 # Stephane Chazelas 建议使用下边

37 #+ 的更好方法来检测命令行参数.

38 #+ 但对于这章来说还是有点超前.

39 #

40 # E_WRONGARGS=65 # 非数值参数(错误的参数格式)

41 #

42 # case "$1" in

43 # "" ) lines=50;;

44 # *[!0-9]*) echo "Usage: `basename $0` file-to-cleanup"; exit $E_WRONGARGS;;

45 # * ) lines=$1;;

46 # esac

47 #

48 #* 直到"Loops"的章节才会对上边的内容进行详细的描述.

49

50

51 cd $LOG_DIR

52

53 if [ `pwd` != "$LOG_DIR" ] # 或者 if[ "$PWD" != "$LOG_DIR" ]

54 # 不在 /var/log中?

55 then

56 echo "Can't change to $LOG_DIR."

57 exit $E_XCD

58 fi # 在处理log file之前,再确认一遍当前目录是否正确.

59

60 # 更有效率的做法是

61 #

62 # cd /var/log || {

63 # echo "Cannot change to necessary directory." >&2

64 # exit $E_XCD;

65 # }

66

67

68

69

70 tail -$lines messages > mesg.temp # 保存log file消息的最后部分.

71 mv mesg.temp messages # 变为新的log目录.

72

73

74 # cat /dev/null > messages

75 #* 不再需要了,使用上边的方法更安全.

76

77 cat /dev/null > wtmp # ': > wtmp' 和 '> wtmp'具有相同的作用

78 echo "Logs cleaned up."

79

80 exit 0

81 # 退出之前返回0,返回0表示成功.

82 #

################################End Script#########################################


因为你可能希望将系统log全部消灭,这个版本留下了log消息最后的部分.你将不断地找到新

的方法来完善这个脚本,并提高效率.


要注意,在每个脚本的开头都使用"#!",这意味着告诉你的系统这个文件的执行需要指定一个解

释器.#!实际上是一个2字节[1]的魔法数字,这是指定一个文件类型的特殊标记, 换句话说, 在

这种情况下,指的就是一个可执行的脚本(键入man magic来获得关于这个迷人话题的更多详细

信息).在#!之后接着是一个路径名.这个路径名指定了一个解释脚本中命令的程序,这个程序可

以是shell,程序语言或者是任意一个通用程序.这个指定的程序从头开始解释并且执行脚本中

的命令(从#!行下边的一行开始),忽略注释.[2]

如:

1 #!/bin/sh

2 #!/bin/bash

3 #!/usr/bin/perl

4 #!/usr/bin/tcl

5 #!/bin/sed -f

6 #!/usr/awk -f


上边每一个脚本头的行都指定了一个不同的命令解释器,如果是/bin/sh,那么就是默认shell

(在Linux系统中默认是Bash).[3]使用#!/bin/sh,在大多数商业发行的UNIX上,默认是Bourne

shell,这将让你的脚本可以正常的运行在非Linux机器上,虽然这将会牺牲Bash一些独特的特征.

脚本将与POSIX[4] 的sh标准相一致.


注意: #! 后边给出的路径名必须是正确的,否则将会出现一个错误消息,通常是

"Command not found",这将是你运行这个脚本时所得到的唯一结果.


当然"#!"也可以被忽略,不过这样你的脚本文件就只能是一些命令的集合,不能够使用shell内建

的指令了,如果不能使用变量的话,当然这也就失去了脚本编程的意义了.


注意:这个例子鼓励你使用模块化的方式来编写脚本,平时也要注意收集一些零碎的代码,

这些零碎的代码可能用在你将来编写的脚本中.这样你就可以通过这些代码片段来构

造一个较大的工程用例. 以下边脚本作为序,来测试脚本被调用的参数是否正确.

################################Start Script#######################################

1 E_WRONG_ARGS=65

2 script_parameters="-a -h -m -z"

3 # -a = all, -h = help, 等等.

4

5 if [ $# -ne $Number_of_expected_args ]

6 then

7 echo "Usage: `basename $0` $script_parameters"

8 # `basename $0`是这个脚本的文件名

9 exit $E_WRONG_ARGS

10 fi

################################End Script#########################################

大多数情况下,你需要编写一个脚本来执行一个特定的任务,在本章中第一个脚本就是一个这样

的例子, 然后你会修改它来完成一个不同的,但比较相似的任务.用变量来代替写死的常量,就是

一个好方法,将重复的代码放到一个函数中,也是一种好习惯.



2.1 调用一个脚本

----------------

编写完脚本之后,你可以使用sh scriptname,[5]或者bash scriptname来调用它.

(不推荐使用sh <scriptname,因为这禁用了脚本从stdin中读数据的功能.)

更方便的方法是让脚本本身就具有可执行权限,通过chmod命令可以修改.


比如:

chmod 555 scriptname (允许任何人都具有 可读和执行权限) [6]

或:

chmod +rx scriptname (允许任何人都具有 可读和执行权限)

chmod u+rx scriptname (只给脚本的所有者 可读和执行权限)


既然脚本已经具有了可执行权限,现在你可以使用./scriptname.[7]来测试它了.如果这个脚本

以一个"#!"行开头,那么脚本将会调用合适的命令解释器来运行.


最后一步,在脚本被测试和debug之后,你可能想把它移动到/usr/local/bin(当然是以root身份)

,来让你的脚本对所有用户都有用.这样用户就可以直接敲脚本名字来运行了.


注意事项:

[1] 那些具有UNIX味道的脚本(基于4.2BSD)需要一个4字节的魔法数字,在#!后边需要一个

空格#! /bin/sh.

[2] 脚本中的#!行的最重要的任务就是命令解释器(sh或者bash).因为这行是以#开始的,

当命令解释器执行这个脚本的时候,会把它作为一个注释行.当然,在这之前,这行语句

已经完成了它的任务,就是调用命令解释器.


如果在脚本的里边还有一个#!行,那么bash将把它认为是一个一般的注释行.

1 #!/bin/bash

2

3 echo "Part 1 of script."

4 a=1

5

6 #!/bin/bash

7 # 这将不会开始一个新脚本.

8

9 echo "Part 2 of script."

10 echo $a # Value of $a stays at 1.

[3] 这里可以玩一些小技巧.

1 #!/bin/rm

2 # 自删除脚本.

3

4 # 当你运行这个脚本时,基本上什么都不会发生...除非这个文件消失不见.

5

6 WHATEVER=65

7

8 echo "This line will never print (betcha!)."

9

10 exit $WHATEVER # 没关系,脚本是不会在这退出的.

当然,你还可以试试在一个README文件的开头加上#!/bin/more,并让它具有执行权限.

结果将是文档自动列出自己的内容.(一个使用cat命令的here document可能是一个

更好的选则,--见Example 17-3).

[4] 可移植的操作系统接口,标准化类UNIX操作系统的一种尝试.POSIX规范可以在

http://www.opengroup.org/onlinepubs/007904975/toc.htm中查阅.

[5] 小心:使用sh scriptname来调用脚本的时候将会关闭一些Bash特定的扩展,脚本可能

因此而调用失败.

[6] 脚本需要读和执行权限,因为shell需要读这个脚本.

[7] 为什么不直接使用scriptname来调用脚本?如果你当前的目录下($PWD)正好有你想要

执行的脚本,为什么它运行不了呢?失败的原因是,出于安全考虑,当前目录并没有被

加在用户的$PATH变量中.因此,在当前目录下调用脚本必须使用./scriptname这种

形式.



2.2 初步的练习

--------------

1. 系统管理员经常会为了自动化一些常用的任务而编写脚本.举出几个这种有用的脚本的实例.

2. 编写一个脚本,显示时间和日期,列出所有的登录用户,显示系统的更新时间.然后这个脚本

将会把这些内容保存到一个log file中.


相关阅读 更多 +
排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载