控制流结构_VMware, Unix及操作系统讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  VMware, Unix及操作系统讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 4038 | 回复: 0   主题: 控制流结构        下一篇 
匿名用户
发表于: IP:您无权察看 2011-8-2 15:19:18 | [全部帖] [楼主帖] 楼主

所有功能脚本必须有能力进行判断,也必须有能力基于一定条件处理相关命令。本章讲述这方面的功能,在脚本中创建和应用控制结构。
本章内容有:

    退出状态。
while、f o r和until loops循环。
if then else语句。

    脚本中动作。
 菜单

退出状态
在书写正确脚本前,大概讲一下退出状态。任何命令进行时都将返回一个退出状态。如
果要观察其退出状态,使用最后状态命令:

$ echo $?


主要有4种退出状态。前面已经讲到了两种,即最后命令退出状态$ ?和控制次序命令( $ $、
| |)。其余两种是处理s h e l l脚本或s h e l l退出及相应退出状态或函数返回码。在第1 9章讲到函数
时,也将提到其返回码。
要退出当前进程,s h e l l提供命令e x i t,一般格式为:

exit n


其中,n为一数字。
如果只在命令提示符下键入e x i t,假定没有在当前状态创建另一个s h e l l,将退出当前s h e l l。
如果在脚本中键入e x i t,s h e l l将试图(通常是这样)返回上一个命令返回值。有许多退出脚本
值,但其中相对于脚本和一般系统命令最重要的有两种,即:
退出状态0 退出成功,无错误。
退出状态1 退出失败,某处有错误。
可以在s h e l l脚本中加入自己的退出状态(它将退出脚本)。本书鼓励这样做,因为另一个
s h e l l脚本或返回函数可能要从s h e l l脚本中抽取退出脚本。另外,相信加入脚本本身的退出脚
本值是一种好的编程习惯。
如果愿意,用户可以在一个用户输入错误后或一个不可覆盖错误后或正常地处理结束后
退出脚本。
注意从现在起,本书所有脚本都将加入注释行。注释行将解释脚本具体含义,帮助用户
理解脚本。可以在任何地方加入注释行,因为其本身被解释器忽略。注释行应以#开头。

控制结构
几乎所有的脚本里都有某种流控制结构,很少有例外。流控制是什么?假定有一个脚本
包含下列几个命令:

#!/bin/sh
# make a directory
mkdir /home/dave/mydocs
# copy all doc files
cp *.docs /home/dave/docs
# delete all doc files
rm *.docs


上述脚本问题出在哪里?如果目录创建失败或目录创建成功文件拷贝失败,如何处理?
这里需要从不同的目录中拷贝不同的文件。必须在命令执行前或最后的命令退出前决定处理
方法。s h e l l会提供一系列命令声明语句等补救措施来帮助你在命令成功或失败时,或需要处
理一个命令清单时采取正确的动作。
这些命令语句大概分两类:
循环和流控制。

流控制
i f、t h e n、e l s e语句提供条件测试。测试可以基于各种条件。例如文件的权限、长度、数
值或字符串的比较。这些测试返回值或者为真( 0),或者为假( 1)。基于此结果,可以进行
相关操作。在讲到条件测试时已经涉及了一些测试语法。
c a s e语句允许匹配模式、单词或值。一旦模式或值匹配,就可以基于这个匹配条件作其他
声明。

循环
循环或跳转是一系列命令的重复执行过程,本书提到了3种循环语句:
for 循环每次处理依次列表内信息,直至循环耗尽。
Until 循环此循环语句不常使用, u n t i l循环直至条件为真。条件部分在循环末尾部分。
While 循环w h i l e循环当条件为真时,循环执行,条件部分在循环头。
流控制语句的任何循环均可嵌套使用,例如可以在一个f o r循环中嵌入另一个f o r循环。
现在开始讲解循环和控制流,并举一些脚本实例。
从现在起,脚本中e c h o语句使用L I N U X或B S D版本,也就是说使用e c h o方法echo -e -n,
意即从e c h o结尾中下一行执行命令。应用于U N I X(系统V和B S D)的统一的e c h o命令参阅1 9
章s h e l l函数。

if then else语句
i f语句测试条件,测试条件返回真( 0)或假(1)后,可相应执行一系列语句。i f语句结
构对错误检查非常有用。其格式为:
if 条件1
then 命令1
elif 条件2
then 命令2
else 命令3

f i


让我们来具体讲解i f语句的各部分功能。
If 条件1 如果条件1为真
Then 那么
命令1 执行命令1
elif 条件2 如果条件1不成立
then 那么
命令2 执行命令2
else 如果条件1,2均不成立
命令3 那么执行命令3
fi 完成
i f语句必须以单词f i终止。在i f语句中漏写f i是最一般的错误。我自己有时也是这样。
e l i f和e l s e为可选项,如果语句中没有否则部分,那么就不需要e l i f和e l s e部分。I f语句可以
有许多e l i f部分。最常用的i f语句是if then fi结构。

if...then是最常见的条件判断语句,简而言之,就是符合某个条件判断的时候,就予以进行某项工作就是了,我们可以简单地这样子看:
if [ 条件判断表达式 ]; then
            当条件判断表达式成立时可以执行的命令

fi


较特别的是,如果有多个条件判断时,可以将多个判断写入一个中括号,还可以使用多个中括号隔开,而括号和括号之间,则以&&或||来隔开,这个要和命令行中的&&,||区别开来。

# cat iftest
#!/bin/sh
# iftest
# this is a comment line, all commment lines start with a #
if [ "10" -lt "12" ]
then
# yes 10 is less than 12
echo "Yes, 10 is less than 12"
fi


变量值测试
通过测试设置为接受用户输入的变量可以测知用户是否输入信息。下面的例子中测试用户键入
r e t u r n键后变量n a m e是否包含任何信息。

# cat iftest2.sh
#!/bin/sh
# iftest2
echo -n "Enter your name :"
read NAME
# did the user just hit return ???
if [ "$NAME" == "" ]; then
echo "You did not enter any information"
fi
#./iftest2.sh
Enter your name :
You did not enter any information


grep输出检查
不必拘泥于变量或数值测试,也可以测知系统命令是否成功返回。对g r e p使用i f语句找出
g r e p是否成功返回信息。下面的例子中g r e p用于查看D a v e是否在数据文件d a t a . f i l e中,注意
‘D a v e \ >’用于精确匹配
grep输出检查
不必拘泥于变量或数值测试,也可以测知系统命令是否成功返回。对g r e p使用i f语句找出
g r e p是否成功返回信息。下面的例子中g r e p用于查看D a v e是否在数据文件d a t a . f i l e中,注意
‘D a v e \ >’用于精确匹配

[root@localhost ~]# cat grepif.sh
#!/bin/sh
# grepif.sh
if grep 'Dave\>' data.file > /dev/null 2>&1
then
echo "Great Dave is in the file"
else
echo "No Dave is not in the file"
fi
[root@localhost ~]# ./grepif.sh
No Dave is not in the file


用变量测试grep输出
正像前面看到的,可以用g r e p作字符串操作。下面的脚本中,用户输入一个名字列表,
g r e p在变量中查找,要求其包含人名P e t e r。
下面是具体的代码情况和运行信息。

[root@localhost ~]# cat grepstr.sh
#!/bin/sh
# grepstr
echo -n "Enter a list of names:"
read list
if echo $list | grep "Peter" > /dev/null 2>&1
then
echo "Peter is here"
# could do some processing here...
else
echo "Peter's not in the list. No comment!"
fi
[root@localhost ~]# ./grepstr.sh
Enter a list of names:John Louise Peter James
Peter is here


文件拷贝输出检查
下面测试文件拷贝是否正常,如果c p命令并没有拷贝文件m y f i l e到m y f i l e . b a k,则打印错
误信息。注意错误信息中` basename $0`打印脚本名。
如果脚本错误退出,一个好习惯是显示脚本名并将之定向到标准错误中。用户应该知道
产生错误的脚本名。

[root@localhost ~]# cat ifcp.sh
#!/bin/sh
# ifcp.sh
if cp myfile myfile.bak; then
echo "good copy"
else
echo "`basename $0`: error could not copy the file" >&2
fi
[root@localhost ~]# ./ifcp.sh
cp: cannot stat `myfile': No such file or directory
ifcp.sh: error could not copy the file


注意,文件可能没找到,系统也产生本身的错误信息,这类错误信息可能与输出混在一
起。既然已经显示系统错误信息获知脚本失败,就没必要显示两次。要去除系统产生的错误
和系统输出,只需简单的将标准错误和输出重定向即可。修改脚本为: >/dev/null 2>&1。

[root@localhost ~]# cat ifcp.sh
#!/bin/sh
# ifcp.sh
if cp myfile myfile.bak > /dev/null 2>&1; then
echo "good copy"
else
echo "`basename $0`: error could not copy the file" >&2
fi
[root@localhost ~]# ./ifcp.sh
ifcp.sh: error could not copy the file


上面当中>/dev/null表示任何标准输出都定向到那个无尽的“黑洞”/de/null中,然后2>&1表示
错误输出也是到/dev/null中,&1表示前面的那个/dev/null,脚本运行时,所有输出包括错误重定向至系统垃圾堆。

当前目录测试
当运行一些管理脚本时,可能要在根目录下运行它,特别是移动某种全局文件或进行权
限改变时。一个简单的测试可以获知是否运行在根目录下。下面脚本中变量D I R E C TO RY使用
当前目录的命令替换操作,然后此变量值与" / "字符串比较( /为根目录)。如果变量值与字符
串不等,则用户退出脚本,退出状态为1意味错误信息产生。

[root@localhost ~]# cat ifpwd.sh
#!/bin/sh
# ifpwd.sh
DIRECTORY=`pwd`
# grab the current dirctory
if [ "$DIRECTORY" != "/" ]; then
# is it the root directory ?
# no, the direct output to standard error, which is the screen
# by default.
echo "You need to be in the root directory no $DIRECTORY to run
this script" >&2
# exit with a value of 1, an error
exit 1
fi
[root@localhost ~]# ./ifpwd.sh
You need to be in the root directory no /root to run
this script


文件权限测试
可以用i f语句测试文件权限,下面简单测试文件t e s t . t x t是否被设置到变量L O G N A M E,测试test.txt文件是否具有写的权限。下面的脚本先建立一个test.txt的空白文档,列出它的相关权限。然后执行脚本测试其是否可以写入,然后显示相关信息。

[root@localhost ~]# touch test.txt
[root@localhost ~]# ls -l test.txt
-rw-r--r-- 1 root root 0 Nov 21 15:21 test.txt
[root@localhost ~]# chmod u+x ifwr.sh
[root@localhost ~]# cat ifwr.sh
#!/bin/sh
# ifwr.sh
LOGFILE=test.txt
echo $LOGFILE
if [ ! -w "$LOGFILE" ]; then
echo " You cannot write to $LOGFILE" >&2
else
echo " You can write to $LOGFILE" >&2
fi
[root@localhost ~]# ./ifwr.sh
test.txt
You can write to test.txt


测试传递到脚本中的参数
i f语句可用来测试传入脚本中参数的个数。使用特定变量$ #,表示调用参数的个数。可以
测试所需参数个数与调用参数个数是否相等。
以下测试确保脚本有三个参数。如果没有,则返回一个可用信息到标准错误,然后代码
退出并显示退出状态。如果参数数目等于3,则显示所有参数。

[root@localhost ~]# cat ifparam.sh
#!/bin/sh
# ifparam
if [ $# -lt 3 ]; then
# less than 3 parameters called, echo a usage message and exit
# 如果少于三个参数则显示使用的信息,然后退出。
echo "Usage: `basename $0`arg1 arg2 arg3" >&2
exit 1
fi
# good, received 3 params, let's echo them
# 好,现在接受了三个参数,让我们开始显示他们
echo "arg1: $1"
echo "arg2: $2"
echo "arg3: $3"
[root@localhost ~]# ./ifparam.sh cup medal
Usage: ifparam.sharg1 arg2 arg3
[root@localhost ~]# ./ifparam.sh cup medal trophy
arg1: cup
arg2: medal
arg3: trophy


从上面的运行信息可以看出,如果只传入两个参数,则显示一可用信息,然后脚本退出。
只有正确传入了三个参数了,才显示所有的参数然后退出。

决定脚本是否为交互模式
有时需要知道脚本运行是交互模式(终端模式)还是非交互模式( c r o n或a t)。脚本也许
需要这个信息以决定从哪里取得输入以及输出到哪里,使用t e s t命令并带有- t选项很容易确认
这一点。如果t e s t返回值为1,则为交互模式。假如我是在一个终端下运行下面这个脚本。

[root@localhost ~]# cat ifinteractive.sh
#!/bin/sh
# ifinteractive.sh
if [ -t ]; then
echo "We are interactive with a terminal"
else
echo "We must be running from some background process probably
cron or at"
fi
[root@localhost ~]# ./ifinteractive.sh
We are interactive with a terminal


简单的if else语句
下一个i f语句有可能是使用最广泛的:
if 条件

t h e n


命令1

e l s e


命令2

f i


使用i f语句的e l s e部分可在条件测试为假时采取适当动作。

变量设置测试
下面的例子测试环境变量E D I TO R是否已设置。如果E D I TO R变量为空,将此信息通知用
户。如果已设置,在屏幕上显示编辑类型。

[root@localhost ~]# cat ifeditor.sh
#!/bin/sh
# ifeditor.sh
if [ -z $EDITOR ]; then
# the variable has not been set
# 变量没有设置
echo "Your EDITOR environment is not set"
else
# let's see what it is
# 如果设置了,让我们来看看它到底是什么
echo "Using $EDITOR as the default editor"
fi
[root@localhost ~]# ./ifeditor.sh
Your EDITOR environment is not set


将脚本参数传入系统命令
可以向脚本传递位置参数,然后测试变量。这里,如果用户在脚本名字后键入目录名,
脚本将重设$ 1特殊变量为一更有意义的名字。即D I R E C TO RY。这里需测试目录是否为空,如
果目录为空,ls -A将返回空,然后对此返回一信息。

# ifdirec.sh
# assigning $1 to DIRECTORY variable
DIRECTORY=$1
if [ "`ls -A $DIRECTORY`" == "" ]; then
# if it's an empty string, then it's empty
echo "$DIRECTORY is indeed empty"
else
# otherwise it is not
echo "$DIRECTORY is not empty"
fi


也可以使用下面的脚本替代上面的例子并产生同样的结果。

[root@localhost ~]# cat ifdirec2.sh
#!/bin/sh
# ifdirec2
DIRECTORY=$1
if [ -z "`ls -A $DIRECTORY`" ]
then
echo "$DIRECTORY is indeed empty"
else
echo "$DIRECTORY is not empty"
fi


null:命令用法
到目前为止,条件测试已经讲完了t h e n和e l s e部分,有时也许使用者并不关心条件为真或
为假。
不幸的是i f语句各部分不能为空—一些语句已经可以这样做。为解决此问题, s h e l l提供
了:空命令。空命令永远为真(也正是预想的那样)。回到前面的例子,如果目录为空,可以
只在t h e n部分加入命令。

[root@localhost ~]# cat ifdirectory.sh
#!/bin/sh
# ifdirectory.sh
DIRECTORY=$1
if [ "`ls -A $DIRECTORY`" == "" ]
then
echo "$DIRECTORY is indeed empty"
else :
# do nothing
fi
[root@localhost ~]# ./ifdirectory.sh testd
testd is indeed empty


测试目录创建结果
现在继续讨论目录,下面的脚本接受一个参数,并用之创建目录,然后参数被传入命令
行,重设给变量D I R E C TO RY,最后测试变量是否为空。

if ["$DIRECTORY"=""]


也可以用

if[$# -lt 1]


来进行更普遍的参数测试。
如果字符串为空,返回一可用信息,脚本退出。如果目录已经存在,脚本从头至尾走一
遍,什么也没做。
创建前加入提示信息,如果键入Y或y,则创建目录,否则使用空命令表示不采取任何动作。
使用最后命令状态测试创建是否成功执行,如果失败,返回相应信息。

[root@localhost ~]# cat ifmkdir.sh
#!/bin/sh
# ifmkdir.sh
# parameter is passed as $1 but reassigned to DIRECTORY
DIRECTORY=$1
# is the string empty ??
if [ "$DIRECTORY" == "" ]
then
echo "Usage :`basename $0` directory to create" >&2
exit 1
fi
if [ -d $DIRECTORY ]
then : # do nothing
else
echo "The directory does not exist"
echo -n "Create it now? [y..n] :"
read ANS
if [ "$ANS" == "y" ] || [ "$ANS" == "Y" ]
then
echo "creating now"
# create directory and send all output to /dev/null
mkdir $DIRECTORY > /dev/null 2>&1
if [ $? != 0 ]; then
echo "Errors creating the directory $DIRECTORY" >&2
exit 1
fi
else : # do nothing
fi
fi
[root@localhost ~]# ./ifmkdir.sh dt
The directory does not exist
Create it now? [y..n] :y
creating now


另一个拷贝实例
在另一个拷贝实例中,脚本传入两个参数(应该包含文件名),系统命令c p将$ 1拷入$ 2,
输出至/ d e v / n u l l。如果命令成功,则仍使用空命令并且不采取任何动作。
另一方面,如果失败,在脚本退出前要获知此信息。

[root@localhost ~]# cat ifcp2.sh
#!/bin/sh
# ifcp2.sh
if cp $1 $2 > /dev/null 2>&1
# successful, great do nothing
then :
else
# oh dear, show the user what files they were.
echo "`basename $0`: ERROR failed to copy $1 to $2"
exit 1
fi
[root@localhost ~]# ./ifcp2.sh myfile.lex myfile.lex.bak
ifcp2.sh: ERROR failed to copy myfile.lex to myfile.lex.bak
[root@localhost ~]# touch myfile.lex
[root@localhost ~]# ./ifcp2.sh myfile.lex myfile.lex.bak


上面展现了脚本运行成功和脚本运行失败的情况。

下面的脚本用s o r t命令将文件a c c o u n t s . q t r分类,并输出至系统垃圾堆。没人愿意观察屏幕
上3 0 0行的分类页。成功之后不采取任何动作。如果失败,通知用户。

[root@localhost ~]# cat ifsort.sh
#!/bin/sh
# ifsort
if sort accounts.qtr > /dev/null
# sorted. Great
then :
else
# better let the user know
echo "`basename $0`: Oops..errors could not sort accounts.qtr"
fi


多个if语句
可能有时要嵌入i f语句。为此需注意i f和f i的相应匹配使用。

测试和设置环境变量
前面已经举例说明了如何测试环境变量E D I TO R是否被设置。现在如果未设置,则进一步
为其赋值,脚本如下:

#!/bin/sh
# ifseted.sh
# is the EDITOR set?
if [ -z $EDITOR ]; then
echo "Your EDITOR environment is not set"
echo "I will assum you want to use vi .. OK"
echo -n "Do you wish to change it now? [y..n] :"
read ANS
# check for an upper or lower case 'y'
if [ "$ANS" == "Y" ] || [ "$ANS" == "y" ]; then
echo "enter you  editor type :"
read EIDTOR
if [ -z $EDITOR ] || [ "$EDITOR" == "" ]; then
# if EDITOR not set and no value in variable EDITOR,
# then set it to vi
echo "No, editor entered, using vi as default"
EDITOR=vi
export EDITOR
fi
# got a value use it for EDITOR
EDITOR=$EDITOR
export EDITOR
echo "setting $EDITOR"
fi
else
# user
echo "Using vi as the default editor"
EDITOR=vi
export EDITOR
fi


脚本工作方式如下:首先检查是否设置了该变量,如果已经赋值,输出信息提示使用v i作
为缺省编辑器。v i被设置为编辑器,然后脚本退出。
如果未赋值,则提示用户,询问其是否要设置该值。检验用户输入是否为大写或小写y,
输入为其他值时,脚本退出。
如果输入Y或y,再提示输入编辑类型。使用$ E D I TO R =“”测试用户是否未赋值和未点
击r e t u r n键。一种更有效的方法是使用-z $EDITO R方法,本文应用了这两种方法。如果测试
失败,返回信息到屏幕,即使用v i做缺省编辑器,因而E D I TO R赋值为v i。
如果用户输入了一个名字到变量E D I TO R,则使用它作为编辑器并马上让其起作用,即导
出变量E D I TO R。

检测最后命令状态
前面将目录名传入脚本创建了一个目录,脚本然后提示用户是否应创建目录。下面的例
子创建一个目录,并从当前目录将所有* . t x t文件拷入新目录。但是这段脚本中用最后状态命
令检测了每一个脚本是否成功执行。如果命令失败则通知用户。

#!/bin/sh
# ifmkdir2.sh
DIR_NAME=testdirec
# where are we?
THRER=`pwd`
# send all output to the system dustbin
mkdir $DIR_NAME > /dev/null 2>&1
# is it a directory ?
if [ -d $DIR_NAME ]; then
# can we cd to the directory
cd $DIR_NAME
if [ $? == 0 ]; then
# yes we can
HERE=`pwd`
cp $THERE/*.txt $HERE
else
echo "Cannot cd to $DIR_NAME" >&2
exit 1
fi
else
echo "Cannnot create directory $DIR_NAME" >&2
exit 1
fi


增加和检测整数值
下面的例子进行数值测试。脚本包含了一个计数集,用户将其赋予一个新值就可改变它。
脚本然后将当前值1 0 0加入一个新值。工作流程如下:
用户输入一个新值改变其值,如果键入回车键,则不改变它,打印当前值,脚本退出。
如果用户用y或Y响应新值,将提示用户输入增量。如果键入回车键,原值仍未变。键入
一个增量,首先测试是否为数字,如果是,加入计数C O U N TO R中,最后显示新值。

#!/bin/sh
# ifcounter.sh
COUNTER=100
echo "Do you wish to change the counter value currently set at $COUNTER
[y..n] :"
read ANS
if [ "$ANS" == "y" ] || [ "$ANS" == "Y" ]; then
# yes user wants to change the value
echo "Enter a sensible value "
read VALUE
# simple test to see if it's numeric, add any number to VALUE,
# then check out return
# code
expr $VALUE + 10 > /dev/null 2>&1
STATUS=$?
# check return code of expr
if [ "$VALUE" == "" ] || [ "$STATUS" != "0" ]; then
# send errors to standard error
echo " You either entered nothing or a non-numeric " >&2
echo " Sorry now exiting...counter stays at $COUNTER" >&2
exit 1
fi
# if we are here, then it's a number, so add it to COUNTER
COUNTER=`expr $COUNTER + $VALUE`
echo " Counter now set to $COUNTER"
else
# if we are here user just hit return instead of entering a number
# or anssered n to the change a value prompt
echo " Counter stays at $COUNTER"
fi


下面是程序的运行结果:

[root@localhost ~]# ./ifcounter.sh
Do you wish to change the counter value currently set at 100
[y..n] :
n
Counter stays at 100
[root@localhost ~]# ./ifcounter.sh
Do you wish to change the counter value currently set at 100
[y..n] :
y
Enter a sensible value
fdg
You either entered nothing or a non-numeric
Sorry now exiting...counter stays at 100
[root@localhost ~]# ./ifcounter.sh
Do you wish to change the counter value currently set at 100
[y..n] :
y
Enter a sensible value
250
Counter now set to 350


以下是用户登录时启动应用前加入相应安全限制功能的基本框架。首先提示输入用户名
和密码,如果用户名和密码均匹配脚本中相应字符串,用户登录成功,否则用户退出。
脚本首先设置变量为假—总是假定用户输入错误, s t t y当前设置被保存,以便隐藏
p a s s w d域中字符,然后重新保存s t t y设置。
如果用户I D和密码正确(密码是easypasswd),明亮I N VA L I D U S E R和I N VA L I D PA S S W D设
置为n o表示有效用户或密码,然后执行测试,如果两个变量其中之一为y e s,缺省情况下,脚
本退出用户。
键入有效的I D和密码,用户将允许进入。这是一种登录脚本的基本框架。下面的例子中
有效用户I D为root。
提示:在实际中千万不要这么做,任何人都知道用root身份进行测试是很危险的。
以下是用户登录时启动应用前加入相应安全限制功能的基本框架。首先提示输入用户名
和密码,如果用户名和密码均匹配脚本中相应字符串,用户登录成功,否则用户退出。
脚本首先设置变量为假—总是假定用户输入错误, s t t y当前设置被保存,以便隐藏
p a s s w d域中字符,然后重新保存s t t y设置。
如果用户I D和密码正确(密码是easypasswd),明亮I N VA L I D U S E R和I N VA L I D PA S S W D设
置为n o表示有效用户或密码,然后执行测试,如果两个变量其中之一为y e s,缺省情况下,脚
本退出用户。
键入有效的I D和密码,用户将允许进入。这是一种登录脚本的基本框架。下面的例子中
有效用户I D为root。
提示:在实际中千万不要这么做,任何人都知道用root身份进行测试是很危险的.

#!/bin/sh
# ifpass.sh
# set the variables to false
INVALID_USER=yes
INVALID_PASSWD=yes
# save the current stty settings
SAVEDSTTY=`stty -g`
echo "You are logging into a sensitive area"
echo -n "Enter your ID name :"
read NAME
# hide the characters typed in
stty -echo
echo "Enter your password :"
read PASSWORD
# back on again
stty $SAVEDSTTY
if [ "$NAME" == "root" ]; then
# if a valid then set variable
INVALID_USER=no
fi
if [ "$PASSWORD" == "easypasswd" ]; then
# if valid password then set variable
INVALID_PASSWD=no
fi
if [ "$INVALID_USER" == "yes" -o "$INVALID_PASSWD" == "yes" ]; then
echo "`basename $0 :` Sorry wrong password or userid"
exit 1
fi
# if we get here then their ID and password are OK.
echo "correct user id an password given"


下面是对应两种不同情况的输出结果。

[root@localhost ~]# ./ifpass.sh
You are logging into a sensitive area
Enter your ID name :root
Enter your password :
correct user id an password given
[root@localhost ~]# ./ifpass.sh
You are logging into a sensitive area
Enter your ID name :root
Enter your password :
ifpass.sh Sorry wrong password or userid


elif用法
if then else语句的e l i f部分用于测试两个以上的条件。

使用elif进行多条件检测
使用一个简单的例子,测试输入脚本的用户名。脚本首先测试是否输入一个名字,如果
没有,则什么也不做。如果输入了,则用e l i f测试是否匹配r o o t、l o u i s e或d a v e,如果不匹配其
中任何一个,则打印该名字,通知用户不是r o o t、l o u i s e或d a v e。

#!/bin/sh
# ifelif.sh
echo  -n "enter your login name :"
read NAME
# no name entered do not carry on
if [ -z $NAME ] || [ "$NAME" == "" ]; then
echo "You did not enter a name"
elif
# is the name root
[ "$NAME" == "root" ]; then
echo "Hello root"
elif
# or is it louise
[ $NAME == "louise" ]; then
echo "Hello louise"
elif
# or is it dave
[ "$NAME" == "dave" ]; then
echo "Hello dave"
else
# no it's somebody else
echo "You are not root or louise or dave but hi $NAME"
fi


运行上述脚本,给出不同信息,得结果如下:                              

[root@localhost ~]# chmod +x ifelif.sh
[root@localhost ~]# ./ifelif.sh
enter your login name :dave
Hello dave
[root@localhost ~]# ./ifelif.sh
enter your login name :
You did not enter a name
[root@localhost ~]# ./ifelif.sh
enter your login name :Peter
You are not root or louise or dave but hi Peter


多文件位置检测
假定要定位一个用户登录文件,已知此文件在/ u s r / o p t s / a u d i t / l o g s或/ u s r / l o c a l / a u d i t / l o g s中,
具体由其安装人决定。在定位此文件前,首先确保文件可读,此即脚本测试部分。如果未找
到文件或文件不可读,则返回错误信息。脚本如下:

#!/bin/sh
# ifcataudit.sh
# locations of the log file
LOCAT_1=/usr/opts/audit/logs/audit.log
LOCAT_2=/usr/local/audit/audit.logs
if [ -r $LOCAT_1 ]; then
# if it is in this directory and is readable then cat is
echo "Using LOCAT_1"
cat $LOCAT_1
elif
# else it then must be in this direcotory, and is it readable
[ -r $LOCAT_2 ]
then
echo "Using LOCAT_2"
cat $LOCAT_2
else
# not in any of the directories...
echo "`basename $0`: Sorry the audit file is not readable or cannot be
localted." >&2
exit 1
fi


运行上面脚本,如果文件在上述两个目录之一中并且可读,将可以找到它。如果不是,
返回错误并退出,下面结果失败,因为假想的文件并不存在

[root@localhost ~]# ./ifcataudit.sh
ifcataudit.sh: Sorry the audit file is not readable or cannot be
localted.


提示键入y或n
c a s e的一个有效用法是提示用户响应以决定是否继续进程。这里提示输入y以继续处理,n
退出。如果用户输入Y、y或y e s,处理继续执行c a s e语句后面部分。如果用户输入N、n或n o或
其他响应,用户退出脚本。

#!/bin/sh
# caseans.sh
echo -n "Do you wish to proceed [y..n] :"
read ANS
case $ANS in
y|Y|yes|Yes) echo "yes is selected"
;;
n|N) echo "no is selected"
exit 0  # no error so only use exit 0 to terminate
;;
*) echo "`basename $0` : Unknow response" >&2
exit 1
;;
esac
# if we are here then a y|Y|yes|Yes was selected only.


运行脚本,输入无效响应,得结果:

[root@localhost ~]# ./caseans.sh
Do you wish to proceed [y..n] :df
caseans.sh : Unknow response


给出有效响应:

[root@localhost ~]# ./caseans.sh
Do you wish to proceed [y..n] :y
yes is selected


case与命令参数传递
可以使用c a s e控制到脚本的参数传递。
下面脚本中,测试特定变量$ #,它包含传递的参数个数,如果不等于1,退出并显示可用
信息。
然后c a s e语句捕获下列参数: p a s s w d、s t a r t、s t o p或h e l p,相对于每一种匹配模式执行进
一步处理脚本。如果均不匹配,显示可用信息到标准错误输出.

#!/bin/sh
# caseparam.sh
if [ $# != 1 ]; then
echo "Usage:`basename $0`[start|stop|help]" >&2
exit 1
fi
# assign the parameter to the variable OPT
OPT=$1
case $OPT in
start) echo "starting.. `basename $0`"
# code here to start a process
;;
stop) echo "stopping.. `basename $0`"
# code here to stop a process
;;
help)
# code here to display a help page
;;
*) echo "Usage:`basename $0`[start|stop|help]"
;;
esac


运行脚本,输入无效参数.

[root@localhost ~]# ./caseparam.sh what
Usage:caseparam.sh[start|stop|help]


输入有效参数,结果为:

[root@localhost ~]# ./caseparam.sh stop
stopping.. caseparam.sh


捕获输入并执行空命令
不一定要在匹配模式后加入命令,如果你原本不想做什么,只是在进一步处理前过滤出
意外响应,这样做是一种好办法。
如果要运行对应于一个会计部门的帐目报表,必须首先在决定运行报表的类型前确认用
户输入一个有效的部门号,匹配所有可能值,其他值无效。用c a s e可以很容易实现上述功能。
下面的脚本中如果用户输入部门号不是2 3 4、4 5 3、6 5 5或4 5 4,用户退出并返回可用信息。
一旦响应了用户的有效部门号,脚本应用同样的技术取得报表类型,在c a s e语句末尾显示有
效的部门号和报表类型。脚本如下:

#!/bin/sh
# casevaild.sh
echo -n "Enter the account dept No: :"
read ACC
case $ACC in
234);;
453);;
655);;
454);;
*) echo "`basename $0`: Unknown dept No:" >&2
echo "try ... 234,453,655,454"
exit 1
;;
esac
# if we are here, then we have validated the dept no
echo "1 . post"
echo "2 . prior"
echo -n "Enter the type of report:"
read ACC_TYPE
case $ACC_TYPE in
1) TYPE=post
;;
2) TYPE=prior;;
*) echo "`basename $0`: Unknown account type." >&2
exit 1
;;
esac
# if we are here the we are validated!
echo "now running report for dept $ACC for the type $TYPE"
# run the command reprot...


下面是该脚本不同运行输入情况的显示结果。
输入有效部门号:

[root@localhost ~]# ./casevalid.sh
Enter the account dept No: :234
1 . post
2 . prior
Enter the type of report:2
now running report for dept 234 for the type prior


输入无效部门号:

[root@localhost ~]# ./casevalid.sh
Enter the account dept No: :432
casevalid.sh: Unknown dept No:
try ... 234,453,655,454


输入无效的报表类型:

[root@localhost ~]# ./casevalid.sh
Enter the account dept No: :655
1 . post
2 . prior
Enter the type of report:4
casevalid.sh: Unknown account type.


缺省变量值
如果在读变量时输入回车键,不一定总是退出脚本。可以先测试是否已设置了变量,如
果未设置,可以设置该值。
下面的脚本中,要求用户输入运行报表日期。如果用户输入回车键,则使用缺省日期星
期六,并设置为变量w h e n的取值。
如果用户输入另外一天,这一天对于c a s e语句是运行的有效日期,即星期六、星期四、星
期一。注意,这里结合使用了日期缩写作为捕获的可能有效日期。
脚本如下:

#!/bin/sh
# caserep.sh
echo " Weekly Report"
echo -n "What day do you want to run report [Saturday] :"
# if just a return is hit then except default which is Saturday
read WHEN
echo "validating .. ${WHEN:="Saturday"}"
case $WHEN in
Monday|MONDAY|mon)
;;
Sunday|SUNDAY|sun)
;;
Saturday|SATURDAY|sat)
;;
*)
echo "Are you nuts! this report can only be run on " >&2
echo " on a Saturday, Sunday or Monday" >&2
exit 1
;;
esac
echo "Report to run on $WHEN"
# command here to submitted actual report run


对于正确输入:

[root@localhost ~]# ./caserep.sh
Weekly Report
What day do you want to run report [Saturday] :
validating .. Saturday
Report to run on Saturday


对于错误的输入:

[root@localhost ~]# ./caserep.sh
Weekly Report
What day do you want to run report [Saturday] :Tuesday
validating .. Tuesday
Are you nuts! this report can only be run on
on a Saturday, Sunday or Monday


可以推断出c a s e语句有时与if then else 语句功能相同,在某些条件下,这种假定是正确的.

f o r循环一般格式为:
for 变量名i n列表

d o


命令1
命令2?

d o n e


当变量值在列表里, f o r循环即执行一次所有命令,使用变量名访问列表中取值。命令可
为任何有效的s h e l l命令和语句。变量名为任何单词。I n列表用法是可选的,如果不用它, f o r
循环使用命令行的位置参数。
i n列表可以包含替换、字符串和文件名,下面看一些例子。

还有一种常见的for循环的格式是:

for ((初值; 循环条件; 执行步长))
do


            执行的程序段

done


也就是括号中的内容是平常我们熟悉的C语言的风格。

简单的for循环
此例仅显示列表1 2 3 4 5,用变量名访问列表。

#!/bin/sh
# for_i.sh
for loop in 1 2 3 4 5
do
echo $loop
done


运行上述脚本,输出:

[root@localhost ~]# ./for_i.sh
1
2
3
4
5


上面中的1 2 3 4 5序列也可以用下面的方式生成。seq 5或者echo {1..5}的方式。

打印字符串列表
下面f o r循环中,列表包含字符串“ orange red blue grey”,命令为e c h o,变量名为l o o p,
e c h o命令使用$ l o o p反馈出列表中所有取值,直至列表为空。

#!/bin/sh
# forlist.sh
for loop in "orange red blue grey"
do
echo "$loop"
done


运行脚本输出内容是:

orange red blue grey


也可以在循环体中结合使用变量名和字符串。

This is the fruit $loop


其输出结果是:

This is the fruit orange red blue grey


对for循环使用ls命令
这个循环执行l s命令,打印当前目录下所有文件。

#!/bin/sh
# forls.sh
for loop in `ls `
do
echo $loop
done


对for循环使用参数
在f o r循环中省去i n列表选项时,它将接受命令行位置参数作为参数。实际上即指明:

for params in"$@"



for params in"$*"


下面的例子不使用i n列表选项, f o r循环查看特定参数$ @或$ *,以从命令行中取得参数。

#!/bin/sh
# forparam2
for params
do
echo "You supplied $params as a command line option"
done


下面的脚本包含i n"$ @",结果与上面的脚本相同。

#!/bin/sh
# forparam3
for params in "$@"
do
echo "You supplied $params as a command line option"
done


对上述脚本采取进一步动作。如果要查看一系列文件,可在f o r循环里使用f i n d命令,利
用命令行参数,传递所有要查阅的文件。

#!/bin/sh
# forfind.sh
for loop
do
find / -name $loop -print
done


脚本执行时,从命令行参数中取值并使用f i n d命令,这些取值形成- n a m e选项的参数值。

使用for循环连接服务器
因为f o r循环可以处理列表中的取值,现设变量为网络服务器名称,并使用f o r循环连接每一服务器。

#!/bin/sh
# forping.sh
HOSTS="itserv dnssevr acctsmain ladpd ladwareA"
for loop in $HOSTS
do
ping -c 2 $loop
done


多sed删除操作
下面的例子中, s e d用于删除所有空文件,并将输出导至以. H O L D . m v为扩展名的新文件
中,m v将这些文件移至初始文件中。

#!/bin/sh
# forsed.sh
for files in `ls LPSO*`
do
sed -e "/^$/d" $files >$files.HOLD
mv $files.HOLD $files
done


循环计数
前面讨论e x p r时指出,循环时如果要加入计数,使用此命令。下面使用l s在f o r循环中列出
文件及其数目。

#!/bin/sh
# forcount.sh
counter=0
for files in *
do
# increment
counter=`expr $counter + 1`
done
echo "There are $counter files in `pwd` we need to process"


f o r循环一般格式为:
for 变量名i n列表

d o


命令1
命令2?

d o n e


当变量值在列表里, f o r循环即执行一次所有命令,使用变量名访问列表中取值。命令可
为任何有效的s h e l l命令和语句。变量名为任何单词。I n列表用法是可选的,如果不用它, f o r
循环使用命令行的位置参数。
i n列表可以包含替换、字符串和文件名,下面看一些例子。

QUOTE:


还有一种常见的for循环的格式是:

for ((初值; 循环条件; 执行步长))
do


            执行的程序段

done


也就是括号中的内容是平常我们熟悉的C语言的风格。

简单的for循环
此例仅显示列表1 2 3 4 5,用变量名访问列表。

#!/bin/sh
# for_i.sh
for loop in 1 2 3 4 5
do
echo $loop
done


运行上述脚本,输出:

[root@localhost ~]# ./for_i.sh
1
2
3
4
5
QUOTE:


上面中的1 2 3 4 5序列也可以用下面的方式生成。seq 5或者echo {1..5}的方式。

打印字符串列表
下面f o r循环中,列表包含字符串“ orange red blue grey”,命令为e c h o,变量名为l o o p,
e c h o命令使用$ l o o p反馈出列表中所有取值,直至列表为空。

#!/bin/sh
# forlist.sh
for loop in "orange red blue grey"
do
echo "$loop"
done


运行脚本输出内容是:

orange red blue grey


也可以在循环体中结合使用变量名和字符串。

This is the fruit $loop


其输出结果是:

This is the fruit orange red blue grey


对for循环使用ls命令
这个循环执行l s命令,打印当前目录下所有文件。

#!/bin/sh
# forls.sh
for loop in `ls `
do
echo $loop
done


对for循环使用参数
在f o r循环中省去i n列表选项时,它将接受命令行位置参数作为参数。实际上即指明:

for params in"$@"



for params in"$*"


下面的例子不使用i n列表选项, f o r循环查看特定参数$ @或$ *,以从命令行中取得参数。

#!/bin/sh
# forparam2
for params
do
echo "You supplied $params as a command line option"
done


下面的脚本包含i n"$ @",结果与上面的脚本相同。

#!/bin/sh
# forparam3
for params in "$@"
do
echo "You supplied $params as a command line option"
done


对上述脚本采取进一步动作。如果要查看一系列文件,可在f o r循环里使用f i n d命令,利
用命令行参数,传递所有要查阅的文件。

#!/bin/sh
# forfind.sh
for loop
do
find / -name $loop -print
done


脚本执行时,从命令行参数中取值并使用f i n d命令,这些取值形成- n a m e选项的参数值。

使用for循环连接服务器
因为f o r循环可以处理列表中的取值,现设变量为网络服务器名称,并使用f o r循环连接每
一服务器。

#!/bin/sh
# forping.sh
HOSTS="itserv dnssevr acctsmain ladpd ladwareA"
for loop in $HOSTS
do
ping -c 2 $loop
done


使用for循环备份文件
可以用f o r循环备份所有文件,只需将变量作为c p命令的目标参数。这里有一变量. b a k,
当在循环中使用c p命令时,它作为此命令目标文件名。列表命令为l s。

#!/bin/sh
# forbak.sh
suffix=".bak"
for loop in `ls `
do
cp $loop $loop$suffix
done


多文件转换
匹配所有以L P S O开头文件并将其转换为大写。这里使用了l s和c a t命令。l s用于查询出相
关文件, c a t用于将之管道输出至t r命令。目标文件扩展名为.U C,注意在f o r循环中使用l s命令
时反引号的用法。

#!/bin/sh
# forUC.sh
for files in `ls LPSO*`
do
cat $files | tr "[a-z]" "[A-Z]" > $file.UC
done


多sed删除操作
下面的例子中, s e d用于删除所有空文件,并将输出导至以. H O L D . m v为扩展名的新文件
中,m v将这些文件移至初始文件中。

#!/bin/sh
# forsed.sh
for files in `ls LPSO*`
do
sed -e "/^$/d" $files >$files.HOLD
mv $files.HOLD $files
done


循环计数
前面讨论e x p r时指出,循环时如果要加入计数,使用此命令。下面使用l s在f o r循环中列出
文件及其数目。

#!/bin/sh
# forcount.sh
counter=0
for files in *
do
# increment
counter=`expr $counter + 1`
done
echo "There are $counter files in `pwd` we need to process"


脚本的输出结果是:

There are 87 files in /root we need to process


使用w c命令可得相同结果。

[root@localhost ~]# ls | wc -l
87


for循环和本地文档
在f o r循环体中可使用任意命令。下面的例子中,一个变量包含所有当前登录用户。使用
w h o命令并结合a w k语言可实现此功能。然后f o r循环循环每一用户,给其发送一个邮件,邮件
信息部分用一个本地文档完成。

f o r循环一般格式为:
for 变量名i n列表

d o


命令1
命令2?

d o n e


当变量值在列表里, f o r循环即执行一次所有命令,使用变量名访问列表中取值。命令可
为任何有效的s h e l l命令和语句。变量名为任何单词。I n列表用法是可选的,如果不用它, f o r
循环使用命令行的位置参数。
i n列表可以包含替换、字符串和文件名,下面看一些例子。

QUOTE:


还有一种常见的for循环的格式是:

for ((初值; 循环条件; 执行步长))
do


            执行的程序段

done


也就是括号中的内容是平常我们熟悉的C语言的风格。

简单的for循环
此例仅显示列表1 2 3 4 5,用变量名访问列表。

#!/bin/sh
# for_i.sh
for loop in 1 2 3 4 5
do
echo $loop
done


运行上述脚本,输出:

[root@localhost ~]# ./for_i.sh
1
2
3
4
5
QUOTE:


上面中的1 2 3 4 5序列也可以用下面的方式生成。seq 5或者echo {1..5}的方式。

打印字符串列表
下面f o r循环中,列表包含字符串“ orange red blue grey”,命令为e c h o,变量名为l o o p,
e c h o命令使用$ l o o p反馈出列表中所有取值,直至列表为空。

#!/bin/sh
# forlist.sh
for loop in "orange red blue grey"
do
echo "$loop"
done


运行脚本输出内容是:

orange red blue grey


也可以在循环体中结合使用变量名和字符串。

This is the fruit $loop


其输出结果是:

This is the fruit orange red blue grey


对for循环使用ls命令
这个循环执行l s命令,打印当前目录下所有文件。

#!/bin/sh
# forls.sh
for loop in `ls `
do
echo $loop
done


对for循环使用参数
在f o r循环中省去i n列表选项时,它将接受命令行位置参数作为参数。实际上即指明:

for params in"$@"



for params in"$*"


下面的例子不使用i n列表选项, f o r循环查看特定参数$ @或$ *,以从命令行中取得参数。

#!/bin/sh
# forparam2
for params
do
echo "You supplied $params as a command line option"
done


下面的脚本包含i n"$ @",结果与上面的脚本相同。

#!/bin/sh
# forparam3
for params in "$@"
do
echo "You supplied $params as a command line option"
done


对上述脚本采取进一步动作。如果要查看一系列文件,可在f o r循环里使用f i n d命令,利
用命令行参数,传递所有要查阅的文件。

#!/bin/sh
# forfind.sh
for loop
do
find / -name $loop -print
done


脚本执行时,从命令行参数中取值并使用f i n d命令,这些取值形成- n a m e选项的参数值。

使用for循环连接服务器
因为f o r循环可以处理列表中的取值,现设变量为网络服务器名称,并使用f o r循环连接每
一服务器。

#!/bin/sh
# forping.sh
HOSTS="itserv dnssevr acctsmain ladpd ladwareA"
for loop in $HOSTS
do
ping -c 2 $loop
done


使用for循环备份文件
可以用f o r循环备份所有文件,只需将变量作为c p命令的目标参数。这里有一变量. b a k,
当在循环中使用c p命令时,它作为此命令目标文件名。列表命令为l s。

#!/bin/sh
# forbak.sh
suffix=".bak"
for loop in `ls `
do
cp $loop $loop$suffix
done


多文件转换
匹配所有以L P S O开头文件并将其转换为大写。这里使用了l s和c a t命令。l s用于查询出相
关文件, c a t用于将之管道输出至t r命令。目标文件扩展名为.U C,注意在f o r循环中使用l s命令
时反引号的用法。

#!/bin/sh
# forUC.sh
for files in `ls LPSO*`
do
cat $files | tr "[a-z]" "[A-Z]" > $file.UC
done


多sed删除操作
下面的例子中, s e d用于删除所有空文件,并将输出导至以. H O L D . m v为扩展名的新文件
中,m v将这些文件移至初始文件中。

#!/bin/sh
# forsed.sh
for files in `ls LPSO*`
do
sed -e "/^$/d" $files >$files.HOLD
mv $files.HOLD $files
done


循环计数
前面讨论e x p r时指出,循环时如果要加入计数,使用此命令。下面使用l s在f o r循环中列出
文件及其数目。

#!/bin/sh
# forcount.sh
counter=0
for files in *
do
# increment
counter=`expr $counter + 1`
done
echo "There are $counter files in `pwd` we need to process"


脚本的输出结果是:

There are 87 files in /root we need to process


使用w c命令可得相同结果。

[root@localhost ~]# ls | wc -l
87


for循环和本地文档
在f o r循环体中可使用任意命令。下面的例子中,一个变量包含所有当前登录用户。使用
w h o命令并结合a w k语言可实现此功能。然后f o r循环循环每一用户,给其发送一个邮件,邮件
信息部分用一个本地文档完成。

f o r循环一般格式为:
for 变量名i n列表

d o


命令1
命令2?

d o n e


当变量值在列表里, f o r循环即执行一次所有命令,使用变量名访问列表中取值。命令可
为任何有效的s h e l l命令和语句。变量名为任何单词。I n列表用法是可选的,如果不用它, f o r
循环使用命令行的位置参数。
i n列表可以包含替换、字符串和文件名,下面看一些例子。

QUOTE:


还有一种常见的for循环的格式是:

for ((初值; 循环条件; 执行步长))
do


            执行的程序段

done


也就是括号中的内容是平常我们熟悉的C语言的风格。

简单的for循环
此例仅显示列表1 2 3 4 5,用变量名访问列表。

#!/bin/sh
# for_i.sh
for loop in 1 2 3 4 5
do
echo $loop
done


运行上述脚本,输出:

[root@localhost ~]# ./for_i.sh
1
2
3
4
QUOTE:


上面中的1 2 3 4 5序列也可以用下面的方式生成。seq 5或者echo {1..5}的方式。

打印字符串列表
下面f o r循环中,列表包含字符串“ orange red blue grey”,命令为e c h o,变量名为l o o p,
e c h o命令使用$ l o o p反馈出列表中所有取值,直至列表为空。

#!/bin/sh
# forlist.sh
for loop in "orange red blue grey"
do
echo "$loop"
done


运行脚本输出内容是:

orange red blue grey


也可以在循环体中结合使用变量名和字符串。

This is the fruit $loop


其输出结果是:

This is the fruit orange red blue grey


对for循环使用ls命令
这个循环执行l s命令,打印当前目录下所有文件。

#!/bin/sh
# forls.sh
for loop in `ls `
do
echo $loop
done


对for循环使用参数
在f o r循环中省去i n列表选项时,它将接受命令行位置参数作为参数。实际上即指明:

for params in"$@"



for params in"$*"


下面的例子不使用i n列表选项, f o r循环查看特定参数$ @或$ *,以从命令行中取得参数。

#!/bin/sh
# forparam2
for params
do
echo "You supplied $params as a command line option"
done


下面的脚本包含i n"$ @",结果与上面的脚本相同。

#!/bin/sh
# forparam3
for params in "$@"
do
echo "You supplied $params as a command line option"
done


对上述脚本采取进一步动作。如果要查看一系列文件,可在f o r循环里使用f i n d命令,利
用命令行参数,传递所有要查阅的文件。

#!/bin/sh
# forfind.sh
for loop
do
find / -name $loop -print
done


脚本执行时,从命令行参数中取值并使用f i n d命令,这些取值形成- n a m e选项的参数值。

使用for循环连接服务器
因为f o r循环可以处理列表中的取值,现设变量为网络服务器名称,并使用f o r循环连接每
一服务器。

#!/bin/sh
# forping.sh
HOSTS="itserv dnssevr acctsmain ladpd ladwareA"
for loop in $HOSTS
do
ping -c 2 $loop
done


使用for循环备份文件
可以用f o r循环备份所有文件,只需将变量作为c p命令的目标参数。这里有一变量. b a k,
当在循环中使用c p命令时,它作为此命令目标文件名。列表命令为l s。

#!/bin/sh
# forbak.sh
suffix=".bak"
for loop in `ls `
do
cp $loop $loop$suffix
done


多文件转换
匹配所有以L P S O开头文件并将其转换为大写。这里使用了l s和c a t命令。l s用于查询出相
关文件, c a t用于将之管道输出至t r命令。目标文件扩展名为.U C,注意在f o r循环中使用l s命令
时反引号的用法。

#!/bin/sh
# forUC.sh
for files in `ls LPSO*`
do
cat $files | tr "[a-z]" "[A-Z]" > $file.UC
done


多sed删除操作
下面的例子中, s e d用于删除所有空文件,并将输出导至以. H O L D . m v为扩展名的新文件
中,m v将这些文件移至初始文件中。

#!/bin/sh
# forsed.sh
for files in `ls LPSO*`
do
sed -e "/^$/d" $files >$files.HOLD
mv $files.HOLD $files
done


循环计数
前面讨论e x p r时指出,循环时如果要加入计数,使用此命令。下面使用l s在f o r循环中列出
文件及其数目。

#!/bin/sh
# forcount.sh
counter=0
for files in *
do
# increment
counter=`expr $counter + 1`
done
echo "There are $counter files in `pwd` we need to process"


脚本的输出结果是:

There are 87 files in /root we need to process


使用w c命令可得相同结果。

[root@localhost ~]# ls | wc -l
87


for循环和本地文档
在f o r循环体中可使用任意命令。下面的例子中,一个变量包含所有当前登录用户。使用
w h o命令并结合a w k语言可实现此功能。然后f o r循环循环每一用户,给其发送一个邮件,邮件
信息部分用一个本地文档完成。

脚本的输出结果是:

There are 87 files in /root we need to process


使用w c命令可得相同结果。

[root@localhost ~]# ls | wc -l
87


for循环和本地文档
在f o r循环体中可使用任意命令。下面的例子中,一个变量包含所有当前登录用户。使用
w h o命令并结合a w k语言可实现此功能。然后f o r循环循环每一用户,给其发送一个邮件,邮件
信息部分用一个本地文档完成.

#!/bin/sh
# formailit.sh
WHOS_ON=`who -u | awk '{print $1}'`
for user in $WHOS_ON
do
mail $user << MAYDAY
Dear Colleagues,
It's my birthday today, see you down the
club at 17:30 for a drink.
See ya.
$LOGNAME
MAYDAY
Done


for循环嵌入
嵌入循环可以将一个f o r循环嵌在另一个f o r循环内:
for 变量名1 in列表1

d o


for 变量名2 in 列表2

d o


命令1

. . .
d o n e
d o n e


下面脚本即为嵌入f o r循环,这里有两个列表A P P S和S C R I P T S。第一个包含服务器上应用
的路径,第二个为运行在每个应用上的管理脚本。对列表A P P S上的每一个应用,列表
S C R I P T S里的脚本将被运行,脚本实际上为后台运行。脚本使用t e e命令在登录文件上放一条
目,因此输出到屏幕的同时也输出到一个文件。查看输出结果就可以看出嵌入f o r循环怎样使
用列表S C R I P T S以执行列表A P P S上的处理。

#!/bin/sh
# audit_run.sh
APPS="/apps/accts /apps/claims /apps/stock /apps/serv"
SCRIPTS="audit.check report.run cleanup"
LOGFILE=audit.log
MY_DATE=`date +%H:%M" on "%d/%m%Y`
# outer loop
for loop in $APPS
do
# inner loop
for loop2 in $SCRIPTS
do
echo "system $loop now running $loop2 at $MY_DATE" | tee -a\
$LOGFILE $loop $loop2 &
done
done
tee: /apps/accts: No such file or directory
tee: /apps/accts: No such file or directory
[root@localhost ~]# system /apps/accts now running audit.check at 20:18 on 21/112010
tee: /apps/serv: No such file or directory
system /apps/serv now running report.run at 20:18 on 21/112010
tee: /apps/serv: No such file or directory
system /apps/serv now running cleanup at 20:18 on 21/112010


until循环
u n t i l循环执行一系列命令直至条件为真时停止。u n t i l循环与w h i l e循环在处理方式上刚好
相反。一般w h i l e循环优于u n t i l循环,但在某些时候—也只是极少数情况下, u n t i l循环更加
有用。
u n t i l循环格式为:
until 条件
命令1

. . .
d o n e


条件可为任意测试条件,测试发生在循环末尾,因此循环至少执行一次—请注意这一
点。
下面是一些实例。
简单的until循环
这段脚本不断的搜寻w h o命令中用户r o o t,变量I S - R O O T保存g r e p命令结果。
如果找到了r o o t,循环结束,并向用户s i m o n发送邮件,通知他用户r o o t已经登录,注意
这里s l e e p命令用法,它经常用于u n t i l循环中,因为必须让循环体内命令睡眠几秒钟再执行,
否则会消耗大量系统资源。

#!/bin/sh
# until_who.sh
IS_ROOT=`who | grep root`
until [ "$IS_ROOT" ]
do
sleep 5
done
echo "Watch it. roots in " | mail simon


监视文件
下面例子中, u n t i l循环不断挂起做睡眠,直至文件/ t m p / m o n i t o r. l c k被删除。文件删除后,
脚本进入正常处理过程。

#!/bin/sh
# until_lck.sh
LOCK_FILE=/tmp/process.LCK
until [ ! -f $LOCK_FILE ]
do
sleep 1
done
echo "file deleted "
# normal processing now, file is present


上述例子是使脚本与其他处理过程协调工作的一种方法。还有另外一种方法使脚本间互
相通信。假定有另一段脚本p r o c e s s . m a i n用于搜集本地网络所有机器的信息并将之放入一个报
表文件。
当脚本p r o c e s s . m a i n运行时,创建了一个L C K文件(锁文件),上面脚本必须接收
p r o c e s s . m a i n搜集的信息,但是如果p r o c e s s仍然在修改报表文件时试图处理该文件就不太好
了。
为克服这些问题,脚本p r o c e s s . m a i n创建了一个L C K文件,当它完成时,就删除此文件。
上述脚本将挂起,等待L C K文件被删除,一旦L C K文件删除,上述脚本即可处理报表文
件。

监视磁盘空间
u n t i l循环做监视条件也很有用。假定要监视文件系统容量,当它达到一定水平时通知超
级用户。
下面的脚本监视文件系统/ l o g s,不断从变量$L O O K_O U T中抽取信息, $ L O O K _ O U T包
含使用a w k和g r e p得到的/ l o g s容量。
如果容量达到9 0 %,触发命令部分,向超级用户发送邮件,脚本退出。必须退出,如果
不退出,条件保持为真(例如,容量总是保持在9 0 %以上),将会不断的向超级用户发送邮
件。

#!/bin/sh
# until_mon.sh
# get present column and strip off header row from df
LOOK_OUT=`df | grep /logs | awk '{print $5}' | sed 's/%//g'`
echo $LOOK_OUT
until [ "$LOOK_OUT -gt "90" ]
do
echo "Filesystem..logs is nearly full" | mail root
exit 0
done


while循环
w h i l e循环用于不断执行一系列命令,也用于从输入文件中读取数据,其格式为:
while 命令

d o


命令1
命令2

. . .
d o n e


虽然通常只使用一个命令,但在w h i l e和d o之间可以放几个命令。命令通常用作测试条
件。
只有当命令的退出状态为0时,d o和d o n e之间命令才被执行,如果退出状态不是0,则循
环终止。
命令执行完毕,控制返回循环顶部,从头开始直至测试条件为假。



赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论