Chinaunix首页 | 论坛 | 博客
  • 博客访问: 388856
  • 博文数量: 166
  • 博客积分: 1972
  • 博客等级: 上尉
  • 技术积分: 1845
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-19 21:16
文章分类

全部博文(166)

文章存档

2013年(7)

2010年(159)

分类:

2010-10-19 18:20:39

如果想进入IT行业并管理好自己的主机,那么就必须要学好Shell脚本。Shell脚本有点儿像早期的批处理文件,即将一些命令汇总起来一起执行,但Shell脚本有更强大的功能,它可以运行类似程序的编写,并且不需要编译就能执行。通过Shell脚本来简化日常的管理工作,而且在整个Linux环境中,一些服务的启动都是通过Shell脚本,如果对Shell脚本不了解,发生问题是,会求助无门的。

13.1什么是Shell脚本

Shell脚本是命令号界面下让我们与系统沟通的工具接口。

Shell脚本是利用Shell功能所编写的“程序”,这个程序使用纯文本文件,将一些Shell的语法与命令写在里面,与正则表达式、管道命令以及数据流重导向一起实现我们的目的。还提供了数组,循环,条件与逻辑判断等重要功能,用户可以直接通过Shell编写程序,而不必使用类似C程序语言等传统程序编写的语法。不需编译就可以运行。

13.1.1为什么学习Shell脚本

自动化管理的重要依据。

追踪与管理系统的重要工作,Linux系统的服务启动的接口在 /etc/init.d目录下,都是脚本文件。

简单的入侵检测功能。

连续命名单一化。

简单的数据处理。

跨平台支持与缩短学习历程。

Shell脚本用在系统管理上是很好的工具,但用在处理大量数值运算上就不够了而且还很麻烦(速度慢,占用的CPU资源多)。

13.1.2第一个脚本的编写与执行。

Shell脚本其实就是纯文本文件。

注意事项:

指令的执行是从上而下、从左而右的分析与执行;

指令的下达就如同内提到的: 指令、选项与参数间的多个空白都会被忽略掉;

空白行也将被忽略掉,并且 [tab] 按键所推开的空白同样视为空格键;

如果读取到一个 Enter 符号 (CR) ,就尝试开始执行该行 (或该串) 命令;

至于如果一行的内容太多,则可以使用『 \[Enter] 』来延伸至下一行;

# 』可做为批注!任何加在 # 后面的资料将全部被视为批注文字而被忽略!

 

现在我们假设你写的这个程序文件名是 /home/dmtsai/shell.sh 好了,那如何执行这个档案?可以有底下几个方法:

直接指令下达: shell.sh 档案必须要具备可读与可执行 (rx) 的权限,然后:

绝对路径:使用 /home/dmtsai/shell.sh 来下达指令;

相对路径:假设工作目录在 /home/dmtsai/ ,则使用 ./shell.sh 来执行

变量『PATH』功能:将 shell.sh 放在 PATH 指定的目录内,例如: ~/bin/

 

bash 程序来执行:透过『 bash shell.sh 』或『 sh shell.sh 』来执行

/bin/sh其实就是bin/bash

 

编写第一个脚本

 

[root@www ~]# mkdir scripts; cd scripts

[root@www scripts]# vi sh01.sh

#!/bin/bash

# Program:

#       This program shows "Hello World!" in your screen.

# History:

# 2005/08/23       VBird         First release

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin

export PATH

echo -e "Hello World! \a \n"

exit 0

第一行 #!/bin/bash 在声明这个 script 使用的 shell 名称:
因为我们使用的是 bash ,所以,必须要以『 #!/bin/bash 』来声明这个档案内的语法使用 bash 的语法!那么当这个程序被执行时,他就能够加载 bash 的相关环境设定档 (一般来说就是 ) 并且执行 bash 来使我们底下的指令能够执行!(如果没有设定好这一行, 那么该程序很可能会无法执行,因为系统可能无法判断该程序需要使用什么 shell 来执行啊!)

程序内容的说明:除了第一行的『 #! 』是用来声明 shell 的之外,其它的 # 都是『批注』用途! 所以上面的程序当中,第二行以下就是用来说明整个程序的基本数据。一般来说, 建议你一定要养成说明该 script 的:1. 内容与功能; 2. 版本信息; 3. 作者与联络方式; 4. 建檔日期;5. 历史纪录 等等。这将有助于未来程序的改写与 debug 呢!

主要环境变量的声明:
建议务必要将一些重要的环境变量设定好, PATH LANG (如果有使用到输出相关的信息时) 是当中最重要的! 如此一来,则可让我们这支程序在进行时,可以直接下达一些外部指令,而不必写绝对路径呢!

主要程序部分
就将主要的程序写好即可!在这个例子当中,就是 echo 那一行啦!

(定义回传值)
是否记得我们在里面要讨论一个指令的执行成功与否,可以使用 这个变量来观察~ 那么我们也可以利用 exit 这个指令来让程序中断,并且回传一个数值给系统。利用这个 exit n (n 是数字) 的功能,我们还可以自订错误讯息, 让这支程序变得更加的智能化!

接下来透过刚刚上头介绍的执行方法来执行看看结果吧!

[root@www scripts]# sh sh01.sh

Hello World !

echo 接着那些特殊的按键也可以发生同样的事情~ 不过, echo 必须要加上 -e 的选项才行!另外,你也可以利用:『chmod a+x sh01.sh; ./sh01.sh』来执行这个 script 的呢!

13.1.3编写Shell脚本的良好习惯

在每个脚本的头文件处记录:

脚本的功能

脚本的版本信息

脚本的作者与联系方式

脚本的版权声明

脚本的历史记录

脚本内特殊的命令,使用绝对路径的方式来执行

预先声明与设置脚本运行时需要的环境变量。

13.2 简单的Shell脚本练习

1变量的内容由用户自己决定

2利用date建立文件

3数值运算的方法

13.3善用判断条件

13.3.1使用test命令的测试功能

要检测某些文件或者相关属性时,使用test命令即可。

例子:要检测/cjw是否存在 可以这样

test –e /cjw 当然这样的执行结果是不会显示任何消息的,不过可以通过$?还有&& ||结合起来整体显示结果。上面的例子可以改写成这样:test –e /cjw && echo “exist”|| echo “not exist”

-e是测试一个的“东西”是否存在。

测试的标志

代表意义

1. 关于某个文件名的『文件类型』判断,如 test -e filename 表示存在否

-e

该『档名』是否存在?(常用)

-f

该『档名』是否存在且为档案(file)(常用)

-d

该『文件名』是否存在且为目录(directory)(常用)

-b

该『档名』是否存在且为一个 block device 设备?

-c

该『档名』是否存在且为一个 character device 设备?

-S

该『档名』是否存在且为一个 Socket 文件?

-p

该『档名』是否存在且为一个 FIFO (pipe) 文件?

-L

该『档名』是否存在且为一个连结文件?

2. 关于文件的权限侦测,如 test -r filename 表示可读否 ( root 权限常有例外)

-r

侦测该文件名是否存在且具有『可读』的权限?

-w

侦测该文件名是否存在且具有『可写』的权限?

-x

侦测该文件名是否存在且具有『可执行』的权限?

-u

侦测该文件名是否存在且具有『SUID』的属性?

-g

侦测该文件名是否存在且具有『SGID』的属性?

-k

侦测该文件名是否存在且具有『Sticky bit』的属性?

-s

侦测该档名是否存在且为『非空白档案』?

3. 两个文件之间的比较,如: test file1 -nt file2

-nt

(newer than)判断 file1 是否比 file2

-ot

(older than)判断 file1 是否比 file2

-ef

判断 file1 file2 是否为同一文件,可用在判断 hard link 的判定上。 主要意义在判定,两个文件是否均指向同一个 inode 哩!

4. 关于两个整数之间的判定,例如 test n1 -eq n2

-eq

两数值相等 (equal)

-ne

两数值不等 (not equal)

-gt

n1 大于 n2 (greater than)

-lt

n1 小于 n2 (less than)

-ge

n1 大于等于 n2 (greater than or equal)

-le

n1 小于等于 n2 (less than or equal)

5. 判定字符串的数据

test -z string

判定字符串是否为 0 ?若 string 为空字符串,则为 true

test -n string

判定字符串是否非为 0 ?若 string 为空字符串,则为 false
注: -n 亦可省略

test str1 = str2

判定 str1 是否等于 str2 ,若相等,则回传 true

test str1 != str2

判定 str1 是否不等于 str2 ,若相等,则回传 false

6. 多重条件判定,例如: test -r filename -a -x filename

-a

(and)两状况同时成立!例如 test -r file -a -x file,则 file 同时具有 r x 权限时,才回传 true

-o

(or)两状况任何一个成立!例如 test -r file -o -x file,则 file 具有 r x 权限时,就可回传 true

!

反相状态,如 test ! -x file ,当 file 不具有 x 时,回传 true

13.3.2 使用判断符号[ ]

使用判断符号[ ]也可以来进行数据的判断。

使用中括号必须要特别注意,因为中括号用在很多地方,包括通配符与正规表示法等等,所以如果要在 bash 的语法当中使用中括号作为 shell 的判断式时,必须要注意中括号的两端需要有空格符来分隔喔!

[  "$HOME"  ==  "$MAIL"  ]

["$HOME"=="$MAIL"]

                

最好要注意:

在中括号 [ ] 内的每个组件都需要有空格键来分隔;

在中括号内的变数,最好都以双引号括号起来;

在中括号内的常数,最好都以单或双引号括号起来。

13.3.3 Shell脚本的默认变量($0,$1…

shell脚本已经指定好了一些变量:执行文件的文件名为$0变量,第一个参数是$1,。所以,只要在脚本里好好使用$1,就可以执行某些命令了。

有一些特殊意义的参数可以在Shell脚本里面直接使用:

$# :代表后接的参数『个数』,以上表为例这里显示为『 4 』;

$@ :代表『 "$1" "$2" "$3" "$4" 』之意,每个变量是独立的(用双引号括起来)

$* :代表『 "$1c$2c$3c$4" 』,其中 c 为分隔字符,预设为空格键,

利用shift可以移动参数。

13.4 条件判断

13.4.1使用if…then

单层简单的条件判断:

if [ 条件判断式 ]; then

         当条件判断式成立时,可以进行的指令工作内容;

fi   <== if 反过来写,就成为 fi 啦!结束 if 之意!

也就是『将多个条件写入一个中括号内的情况』之外, 还可以有多个中括号来隔开喔!而括号与括号之间,则以 && || 来隔开,他们的意义是:

&& 代表 AND

|| 代表 or

所以,在使用中括号的判断式中, && || 就与指令下达的状态不同了。举例来说:

 [ "$yn" == "Y" -o "$yn" == "y" ]
上式可替换为
[ "$yn" == "Y" ] || [ "$yn" == "y" ]

之所以这样改,很多人是习惯问题!很多人则是喜欢一个中括号仅有一个判别式的原因。好了, 现在我们

多重复杂条件判断式:

在同一个数据的判断中,如果该数据需要进行多种不同的判断时,应该怎么作? 此时你就得要知道底下的语法了:

# 一个条件判断,分成功进行与失败进行 (else)

if [ 条件判断式 ]; then

         当条件判断式成立时,可以进行的指令工作内容;

else

         当条件判断式不成立时,可以进行的指令工作内容;

fi

如果考虑更复杂的情况,则可以使用这个语法:

# 多个条件判断 (if ... elif ... elif ... else) 分多种不同情况执行

if [ 条件判断式一 ]; then

         当条件判断式一成立时,可以进行的指令工作内容;

elif [ 条件判断式二 ]; then

         当条件判断式二成立时,可以进行的指令工作内容;

else

         当条件判断式一与二均不成立时,可以进行的指令工作内容;

fi

你得要注意的是, elif 也是个判断式,因此出现 elif 后面都要接 then 来处理!但是 else 已经是最后的没有成立的结果了, 所以 else 后面并没有 then 喔!

13.4.2使用case…esac判断

case  $变量名称 in   <==关键词为 case ,还有变数前有钱字号

  "第一个变量内容")   <==每个变量内容建议用双引号括起来,关键词则为小括号 )

         程序段

         ;;            <==每个类别结尾使用两个连续的分号来处理!

  "第二个变量内容")

         程序段

         ;;

  *)                  <==最后一个变量内容都会用 * 来代表所有其它值

         不包含第一个变量内容与第二个变量内容的其它程序执行段

         exit 1

         ;;

esac                  <==最终的 case 结尾!『反过来写』思考一下!

要注意的是,这个语法以 case (实际案例之意) 为开头,结尾自然就是将 case 的英文反过来写!就成为 esac 啰! 不难背!

13.4.3利用函数功能

“函数”在Shell脚本里面类似于自定义的执行命令,最大的功能是可以简化很多程序代码。函数的语法是这样的:

function fname() {

         程序段

}

那个 fname 就是我们的自定义的执行命令名称。程序段就是要执行的内容。

注意:在Shell脚本里中,函数的设置一定要在程序的最前面,这样才能在执行时找到可用的程序段。

13.5循环

13.5.1 while do doneuntil do done

一般来说,最常见的循环就是下面这两种:

while [ condition ]  <==中括号内的状态就是判断式

do            <==do 是循环的开始!

         程序段落

done          <==done 是循环的结束

这种方式说的是『当 condition 条件成立时,就进行循环,直到 condition 的条件不成立才停止』的意思。另外一种不定循环的方式:

until [ condition ]

do

         程序段落

done

它说的是『当 condition 条件成立时,就终止循环, 否则就持续进行循环的程序段。』

13.5.2 for…do…done

语法如下:

for (( 初始值; 限制值; 执行步阶 ))

do

         程序段

done

这种语法适合于数值方式的运算当中,在 for 后面的括号内的三串内容意义为:

初始值:某个变量在循环当中的起始值,直接以类似 i=1 设定好;

限制值:当变量的值在这个限制值的范围内,就继续进行循环。例如 i<=100

执行步阶:每作一次循环时,变量的变化量。例如 i=i+1

值得注意的是,在『执行步阶』的设定上,如果每次增加 1 ,可以使用i++的方式。

 

for 这种语法,则是『 已经知道要进行几次循环』的状态,进行非数字方面的循环运行!他的语法是:

for var in con1 con2 con3 ...

do

         程序段

done

以上面的例子来说,这个 $var 的变量内容在循环工作时:

第一次循环时, $var 的内容为 con1

第二次循环时, $var 的内容为 con2

第三次循环时, $var 的内容为 con3

....

例子:

想要让使用者输入某个目录文件名, 然后我找出某目录内的文件名的权限,该如何是好?

[root@www scripts]# vi sh18.sh

#!/bin/bash

# Program:

#       User input dir name, I find the permission of files.

# History:

# 2005/08/29       VBird         First release

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin

export PATH

 

# 1. 先看看这个目录是否存在啊?

read -p "Please input a directory: " dir

if [ "$dir" == "" -o ! -d "$dir" ]; then

         echo "The $dir is NOT exist in your system."

         exit 1

fi

 

# 2. 开始测试档案啰~

filelist=$(ls $dir)        # 列出所有在该目录下的文件名称

for filename in $filelist

do

         perm=""

         test -r "$dir/$filename" && perm="$perm readable"

         test -w "$dir/$filename" && perm="$perm writable"

         test -x "$dir/$filename" && perm="$perm executable"

         echo "The file $dir/$filename's permission is $perm "

done

13.6 Shell脚本的追踪与调试

那么我们如何 debug 呢?有没有办法不需要透过直接执行该 scripts 就可以来判断是否有问题呢?呵呵!当然是有的!我们就直接以 bash 的相关参数来进行判断吧!

[root@www ~]# sh [-nvx] scripts.sh

选项与参数:

-n  :不要执行 script,仅查询语法的问题;

-v  :再执行 sccript 前,先将 scripts 的内容输出到屏幕上;

-x  :将使用到的 script 内容显示到屏幕上,这是很有用的参数!

 

范例一:测试 sh16.sh 有无语法的问题?

[root@www ~]# sh -n sh16.sh

# 若语法没有问题,则不会显示任何信息!

 

范例二:将 sh15.sh 的执行过程全部列出来~

[root@www ~]# sh -x sh15.sh

+ PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/root/bin

+ export PATH

+ for animal in dog cat elephant

+ echo 'There are dogs.... '

There are dogs....

+ for animal in dog cat elephant

+ echo 'There are cats.... '

There are cats....

+ for animal in dog cat elephant

+ echo 'There are elephants.... '

There are elephants....

阅读(590) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~