Until语句
While语句中,只要某条件为真,则重复执行循环代码,until语句正好同while相反,该语句使循环代码重复执行,直到遇到某一条件为真才中止。
Until语句的结构以下:
until command
do
command
command
… …
done
能够用until语句替换上面备份程序的while语句,完成一样的功能:
until [ $ANS != Y -a $ANS != y ]
for 循环
在介绍for循环以前,咱们要学个很是有用的unix命令:shift。咱们知道,对于位置变量或命令行参数,其个数必须是肯定的,或者当Shell程 序不知道其个数时,能够把全部参数一块儿赋值给变量$*。若用户要求Shell在不知道位置变量个数的状况下,还能逐个的把参数一一处理,也就是在$1后 为$2,在$2后面为$3等。在 shift命令执行前变量$1的值在shift命令执行后就不可用了。
示例以下:
#测试shift命令(x_shift.sh)
until [ $# -eq 0 ]
do
echo "第一个参数为: $1 参数个数为: $#"
shift
done
执行以上程序x_shift.sh:
$./x_shift.sh 1 2 3 4
结果显示以下:
第一个参数为: 1 参数个数为: 3
第一个参数为: 2 参数个数为: 2
第一个参数为: 3 参数个数为: 1
第一个参数为: 4 参数个数为: 0
从上可知shift命令每执行一次,变量的个数($#)减一,而变量值提早一位,下面代码用until和shift命令计算全部命令行参数的和。
#shift上档命令的应用(x_shift2.sh)
if [ $# -eq 0 ]
then
echo "Usage:x_shift2.sh 参数"
exit 1
fi
sum=0
until [ $# -eq 0 ]
do
sum=`expr $sum + $1`
shift
done
echo "sum is: $sum"
执行上述程序:
$x_shift2.sh 10 20 15
其显示结果为:
45
shift命令还有另一个重要用途,Bsh定义了9个位置变量,从$1到$9,这并不意味着用户在命令行只能使用9个参数,借助shift命令能够访问多于9个的参数。
Shift命令一次移动参数的个数由其所带的参数指定。例如当shell程序处理完前九个命令行参数后,可使用shift 9命令把$10移到$1。
在熟悉了shift命令后,咱们一块儿看看,Bsh程序中很是有用的for循环语句,这种循环同上面说的while和until循环不一样,for语句中的循环是否执行并不禁某个条件的真和假来决定,决定for循环是否继续的条件是参数表中是否还有未处理的参数。
For语句的结构以下:
for variable in arg1 arg2 … argn
do
command
command
… …
done
下面是for循环的简单例子:
for LETTER in a b c d
do
echo $LETTER
done
程序执行结果以下:
a
b
c
d
在上面计算参数和的例子中,咱们能够用for循环,实现以下:
#测试 for 程序(x_for.sh)
if [ $# -eq 0 ]
then
echo "Usage:x_for.sh 参数… …"
exit 1
fi
sum=0
for I in $*
do
sum=`expr $sum + $I`
done
echo "sum is: $sum"
中断循环指令
在程序循环语句中,咱们有时候但愿遇到某中状况时候结束本次循环执行下次循环或结束这个循环,这就涉及到两条语句:continue和break。 continue命令可以使程序忽略其后循环体中的其余指令,直接进行下次循环,而break命令则马上结束循环,执行循环体后面的的语句。
#测试continue
I=1
while [ $I -lt 10 ]
do
if [ $I -eq 3 ]
then
continue
fi
if [ $I -eq 7 ]
then
break
fi
echo "$I\c"
done
执行上面程序,结果以下:
12456789
与或结构
使用与/或结构有条件的执行命令
Shell程序中可使用多种不一样的方法完成相同的功能,例如until和while语句就能够完成相同的功能,一样,除了if-then-else结 构可使命令有条件的执行外,$$和||操做符也能完成上述功能。在C语言中这两个操做符分别表示逻辑与和逻辑或操做。在Bourne Shell中,用&&链接两条命令的含义只有前面一条命令成功执行了,后面的命令才会执行。
&&操做的形式为:
command && command
例如语句:
rm $TEMPDIR/* && echo "Files successfully removed"
只有rm命令成功执行之后,才会执行echo命令。若用if-then语句实现上述功能,形式为:
if rm $TEMPDIR/*
then
echo "Files successfully removed"
fi
相反,用||链接两条命令的含义为只有第一条命令执行失败才执行第二条命令,例如:
rm $TEMPDIR/* || echo "File were not removed"
上面语句的等价形式为:
if rm $TEMPDIR/*
then
:
else
echo "Files were not removed"
fi
这两种操做符能够联合使用,如在下面的命令行中,只有command1和command2执行成功后,command3才会执行:
command1 && command2 && command3
下面的命令行表示只有command1成功执行,command2不成功执行时,才会执行command3。
&&和||操做符能够简化命令条件执行的格式,但通常只用于一条命令的条件执行。若是许多命令都使用这两个操做符,那么整个程序的可读性将变的不好,因此在多条命令的条件执行时,最好采用可读性好的if语句。
函数
如今咱们介绍Shell程序中的函数部分,基本上任何高级语言都支持函数这个东西,能让咱们胜好多事情的东西,至少省的频繁的敲击相同的东西,好了come onShell程序中的函数
函数又叫作子程序,能够在程序中的任何地方被调用,其格式以下:
函数名字()
{
command
... ...
command;
}
Shell程序的任何地方均可以用命令 "函数名字" 调用,使用函数的好处有两点,一点是使用函数能够把一个复杂的程序化为多个模块,易于管理,符合结构化程序的设计思想,另外一个好处是代码的重用。
Shell函数和Shel程序比较类似,它们的区别在于Shell程序在子Shell中运行,而Shell函数在当前Shell中运行。所以,在当前Shell中能够看到Shell函数对变量的修改。在任何Shell中均可以定义函数,包括交互式Shell。
例如:
$dir() {ls -l;}
结果是咱们在$后面打dir,其显示结果同ls -l的做用是相同的。该dir函数将一直保留到用户从系统退出,或执行了以下所示的unset命令:
$unset dir
下面的例子说明了函数还能够接受位置参数:
$dir(){_
>echo "permission ln owner group file sz last access
>ls -l $*;
>}
运行 dir a* 看产生什么结果
参数a*传递到dir函数中而且代替了$*
一般Shell程序将在子Shell中执行,该程序对变量的改变只在子Shell中有效而在当前Shell中无效。"."命令可使Shell程序在当 前Shell中执行。用户能够在当前Shell中定义函数和对变量赋值。一般用下面命令来从新初使化.profile对Shell环境的设置。
$ . .profile
因为看到这部分相对简单,咱们仍是顺便说说trap好了
使用trap命令进行例外处理
用户编写程序在程序运行时可能会发生一些例外状况,好比执行该程序的用户按中断键或使用kill命令,或者控制终端忽然与系统断开等。unix系统中的 上述状况会使系统向进程发一个信号,一般状况下该信号使进程终止运行。有时侯用户但愿进程在接到终止信号时进行一些特殊的操做。若进程在运行时产生一些临 时文件,又因接受到的信号而终止。那么该进程产生的临时文件将保留下来。在bsh中,用户可使用trap命令修改进程接收到终止信号时进行的默认操做。
trap命令格式以下:
trap command_string signals
多数系统中共有15种发给进程的信号,默认状况下大多数信号都会使程序终止。用户最好查阅本身系统的文挡,看看本系统内使用的信号种类。除了信号为 9(真正的kill信号)不能使用trap命令外,其余信号所带来的操做均可以用trap命令进行指定。下面是trap命令中常用的几种信号:
信号 功能
1 挂起
2 操做中断
15 软终止(kill信号)
若命令串中包含不仅一条命令,必须使用引号将整个命令括起来,具体是单引号仍是双引号,由用户是否须要变量替换决定。" "替换,' '不替换。
使用下面trap命令可使程序在接收到挂起、中断或kill信号时,首先把临时文件删除,而后退出:
trap "rm $TEMPDIR/* $$;exit" 1 2 15
在上面例子中,当Shell读取trap命令时,首先对$TEMPDIR和$$进行变量替换,替换以后的命令串将被保存在trap表中,若上例中 trap命令使用单引号时,trap命令执行时候,不进行变量替换,而把命令串 rm $TEMPDIR/* $$;exit 放到trap表中,当检测到信号时,程序解释执行trap表中的命令串,此时进行变量替换。前面变量$TEMPDIR和$$的值为执行 trap指令时候的值,后一种状况中变量的值为程序接收到信号时候的值,因此 "、'必定要区分仔细。
下面命令的含义为用户按二次中断键后,程序才终止:
trap 'trap 2' 2
通常trap命令中的命令串中几乎都包含exit语句,上面rm的例子若无exit语句,接收到信号rm命令执行完后程序将挂起。但有时用户也须要程序在接到信号后挂起,例如当终端和系统断开后,用户发出挂起信号,并执行空命令,以下:
trap : 1
若用户想取消前trap指令设置的命令串,能够再执行trap命令,在命令中不指定命令串表示接收到信号后进行默认的操做,命令以下:
trap 1
规范Shell
获取UNIX类型的选项:
unix有一个优势就是标准UNIX命令在执行时都具备相同的命令行格式:
command -options parameters
若是在执行Shell程序也采用上述格式,Bourne Shell中提供了一条获取和处理命令行选项的语句,即getopts语句。该语句的格式为:
getopts option_string variable
其中option_string中包含一个有效的单字符选项。若getopts命令在命令行中发现了连字符,那么它将用连字符后面的字符同 option_string相比较。如有匹配,则把变量variable的值设为该选项。若无匹配,则variable设为?。当getopts发现连字 符后面没有字符,会返回一个非零的状态值。Shell程序中能够利用getopts的返回值创建一个循环。
下面代码说明了date命令中怎么使用getopts命令处理各类选项,该程序除了完成unix的标准命令date的功能外,还增长了许多新的选项。
#新date程序
if [ $# -lt 1 ]
then
date
else
while getopts mdyDHMSTJjwahr OPTION
do
case $OPTION
in
m)date '+%m';;
d)date '+%d';;
y)date '+%y';;
D)date '+%D';;
H0date '+%H';;
M)date '+%M';;
S)date '+%S';;
T)date '+%T';;
j)date '+%j';;
J)date '+%y%j';;
w)date '+%w';;
a)date '+%a';;
h)date '+%h';;
r)date '+%r';;
\?)echo "无效的选项!$OPTION";;
esac
done
fi
有时侯选项中还带一个值,getopts命令一样也支持这一功能。这时须要在option_string中选项字母后加一个冒号。当getopts命令 发现冒号后,会从命令行该选项后读取该值。若该值存在,那么将被存在一个特殊的变量OPTARG中。若是该值不存在,getopts命令将在OPTARG 中存放一个问号,而且在标准错误输出上显示一条消息。
下面的例子,实现拷贝一个文件,并给文件赋一个新的名字。-c选项指定程序拷贝的次数,-v选项要求显示新建立文件的文件名。
#--拷贝程序
COPIES=1
VERBOSE=N
while getopts vc:OPTION
do
case $OPTION
in
c)COPIES=$OPTARG;;
v)VERBOSE=Y;;
\?)echo "无效参数!"
exit 1;;
esac
done
if [ $OPTIND -gt $# ]
then
echo "No file name specified"
exit 2
fi
shift 'expr $OPTIND - 1'
FILE=$1
COPY=0
while [ $COPIES -gt $COPY ]
do
COPY='expr $COPY + 1'
cp $FILE $ {FILE} $ {COPY}
if [ VERBOSE = Y }
then
echo ${FILE} $ {COPY}
fi
done
规范Shell:
咱们知道环境变量PS1是提示符,看下面程序chdir:
if [ ! -d "$!" ]
then
echo "$1 is not a directory"
exit 1
fi
cd $1
PS1="'pwd'>"
export PS1
咱们执行:
$chdir /usr/ice666
结果提示符号变成/usr/ice666>了吗?没有,为何?
缘由在于:chdir在子Shell中执行,变量PS1的修改在当前Shell中也不会起做用,若要chdir完成意想中的功能,必须在当前Shell 中执行该命令。最好的方法就是把其改为一个函数而且在.profile文件中定义。但若要把函数放到单个文件中并在当前Shell中执行,则须要使用 . 命令,并将chdir重写成一个函数,把其中的exit改写成return。下面代码是 .ice_ps的内容:
#--提示符
chdir()
{
if [ !-d "$1" ]
then
echo " $1 is not a directory"
return
fi
cd $1
PS1="'pwd'>"
export PS1;
}
而后咱们在.profile文件中加入下面语句
.ice_ps
而后在切换目录的时候,咱们用chdir命令,结果是什么呢,本身实验好了!
调试Shell程序
1>调试shell程序
用户刚编写完Shell程序中,不可避免的会有错误,这时咱们能够利用Bsh中提供的跟踪选项,该选项会显示刚刚执行的命令及参数。用户能够经过set命令打开-x选项或在启动Shell使用-x选项将Shell设置成跟踪模式。例若有下面代码ice_tx:
if [ $# -eq 0 ]
then
echo "usage:sumints integer list"
exit 1
fi
sum=0
until [ $# -eq 0 ]
do
sum='expr $sum + $1'
shift
done
echo $sum
咱们用跟踪模式运行:
$sh -x ice_tx 2 3 4
结果显示:
+[ 3 -eq 0 ]
+sum=0
+[ 3 -eq 0 ]
+expr 0+2
+sum=2
+shift
+[ 2 -eq 0 ]
+expr 2+3
+sum=5
+shift
+[ 1 -eq 0 ]
+expr 5+4
+sum=9
+[ 0 -eq 0 ]
+echo 9
9
从上面能够看出,跟踪模式下Shell显示执行的每一条命令以及该命令使用的变量替换后的参数值。一些控制字如if、then、until等没显示。
2>命令分组
Shell中若干命令能够组成一个单元一块儿执行。为了标识一组命令,这些命令必须放到"()"或"{}"中。放在"()"中的命令将在子Shell中运 行,而放在"{}"中的命令将在当前Shell中运行。子Shell中运行的命令不影响当前Shell的变量。当前Shell中运行的命令影响当前 Shell的变量。
$NUMBER=2
$(A=2;B=2;NUMBER='expr $A+$B';echo $NUMBER)
结果为:4
$echo $NUMBER
结果为:2
若是把上面的()变成{},结果会是怎么样的呢?
3>使用Shell分层管理器shl
UNIX是一个多道程序设计的操做系统,一些UNIX系统利用这一特性提供了Shell层次管理器shl。使用shl用户一次能够打开多个层次的Shell,其中活跃的Shell能够从终端上得到输入。但全部Shell的输出均可在终端上显示,除非显示被禁止。
多个Shell中有一个为shl,当用户在某个Shell中工做时,能够经过使用特殊字符(通常为Ctrl+z)返回shl。为了同其余Shell区 别,shl中提示符为">>>"。当用户工做在Shell层次管理器中时,能够建立、激活和删除Shell,下面是shl中使用的命 令。
create name 产生名为name的层次
delete name 删除名为name的层次
block name 禁止名为name的层次的输出
unblock name 恢复名为name的层次的输出
resume name 激活名为name的层次
toggle 激活近来常用的层次
name 激活名为name的层次
layers [-l] name 对于表中的每一个层次,显示其正在运行的进程的进程号,-l选项要求显示详细信息。
help 显示shl命令的帮助信息
quit 退出shl以及全部被激活的层次
总结
在前面咱们主要介绍了sh的变量、基本语法、程序设计等。若是掌握了这些内容,在学习其余UNIX下编程语言的时候,相信有必定的好处,咱们说了,在大 多数的UNIX中都提供Bourn Shell,并且不多有象sh这样强大的脚本编辑语言了,是系统管理员和程序员的一笔财富,而且不须要额外的软件环境,对文件等处理借助unix命令,实 现起来比c实现还要简单。
原文地址
http://cublog.cn/u/11557/?u=http://cublog.cn/u/11557/showart.php?id=70342