果要观察其退出状态,使用最后状态命令:
$ 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脚本中抽取退出脚本。另外,相信加入脚本本身的退出脚
本值是一种好的编程习惯。
如果愿意,用户可以在一个用户输入错误后或一个不可覆盖错误后或正常地处理结束后
退出脚本。
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循环。
现在开始讲解循环和控制流,并举一些脚本实例。
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结构。
最普通的i f语句是:
i f条件
then 命令
f i
使用i f语句时,必须将t h e n部分放在新行,否则会产生错误。如果要不分行,必须使用命
令分隔符。本书其余部分将采取这种形式。现在简单i f语句变为:
if 条件;t h e n
命令
f i
[root@localhost huangcd]# sh iftest2
enter your name:
your did not enter a information
[root@localhost huangcd]# cat iftest2
#!/bin/bash
echo -n “enter your name:”
read NAME
if [ “$NAME” = “” ];then
echo “your did not enter a information”
fi
下面测试文件拷贝是否正常,如果c p命令并没有拷贝文件m y f i l e到m y f i l e . b a k,则打印错
误信息。注意错误信息中` basename $0`打印脚本名。
如果脚本错误退出,一个好习惯是显示脚本名并将之定向到标准错误中。用户应该知道
产生错误的脚本名。
[root@localhost huangcd]# cat ifcp
#!/bin/bash
if cp ok1.txt ok.bak;then
echo “good copy”
else
echo “`basename $0`:error could not copy the files”>&2
fi
[root@localhost huangcd]# ./ifcp
cp: 无法 stat “ok1.txt”: 没有那个文件或目录
ifcp:error could not copy the files
2代表标准出错,如果文件不存在,如果不加上2>&1则会打印出错信息, 加上以后就不会打印了,也进入那个文件了 &1代表标准输出定向到的地方,这句相当于 cat cities > cities.copy 2> cities.copy 或者 cat cities 1> cities.copy 2> cities.copy 或者 cat cities 2> cities.copy 1> &2 0:标准输入,1:标准输出,2:标准出错
当运行一些管理脚本时,可能要在根目录下运行它,特别是移动某种全局文件或进行权
限改变时。一个简单的测试可以获知是否运行在根目录下。下面脚本中变量D I R E C TO RY使用
当前目录的命令替换操作,然后此变量值与” / “字符串比较( /为根目录)。如果变量值与字符
串不等,则用户退出脚本,退出状态为1意味错误信息产生。
[root@localhost huangcd]# ./ifpwd
you need to be in the root directory.not /home/huangcd
[root@localhost huangcd]# cat ifpwd
#!/bin/bash
DIRCTORY=`pwd`
if [ “$DIRCTORY”!=”/” ]
then echo “you need to be in the root directory.not $DIRCTORY “>&2
exit 1
fi
i f语句可用来测试传入脚本中参数的个数。使用特定变量$ #,表示调用参数的个数。可以
测试所需参数个数与调用参数个数是否相等。
以下测试确保脚本有三个参数。如果没有,则返回一个可用信息到标准错误,然后代码
退出并显示退出状态。如果参数数目等于3,则显示所有参数。
[root@localhost huangcd]# cat ifpatam
#!/bin/bash
if [ $# -lt 3 ]
then echo “Usage:`basename $0` arg1 arg2 arg3” >&2
exit 1
fi
echo “arg1:$1”
echo “arg2:$2”
echo “arg3:$3”
[root@localhost huangcd]# ./ifpatam cup medal
Usage:ifpatam arg1 arg2 arg3
[root@localhost huangcd]# ./ifpatam cup medal thophy
arg1:cup
arg2:medal
arg3:thophy
下面的例子测试环境变量E D I TO R是否已设置。如果E D I TO R变量为空,将此信息通知用
户。如果已设置,在屏幕上显示编辑类型。
[root@localhost huangcd]# cat ifeditor
#!/bin/bash
if [-z $EDITORS ]
then echo “your EDITOR environment is not set”
else
echo “Using $EDITOR as the fefault editor”
fi
[root@localhost huangcd]# ./ifeditor
./ifeditor: line 2: [-z: command not found
Using as the fefault editor
可以向脚本传递位置参数,然后测试变量。这里,如果用户在脚本名字后键入目录名,
脚本将重设$ 1特殊变量为一更有意义的名字。即D I R E C TO RY。这里需测试目录是否为空,如
果目录为空,ls -A将返回空,然后对此返回一信息。
[root@localhost huangcd]# sh ifdirec
is indeed empty
[root@localhost huangcd]# sh ifdirec /home
/home is indeed empty
[root@localhost huangcd]# cat ifdirec
#!/bin/bash
DIRECTORY=$1
if [ “`ls -A $DIRECTORY`”=”” ]
then echo “$DIRECTORY is indeed empty”
else
echo “$DIRECTORY is not empty”
fi
c a s e语句为多选择语句。可以用c a s e语句匹配一个值与一个模式,如果匹配成功,执行相
匹配的命令。c a s e语句格式如下:
case 值i n
模式1 }
命令1
. . .
; ;
模式2)
命令2
. . .
;;
e s a c
c a s e工作方式如上所示。取值后面必须为单词i n,每一模式必须以右括号结束。取值可以
为变量或常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至;;。
取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续
其他模式。如果无一匹配模式,使用星号*捕获该值,再接受其他输入。
模式部分可能包括元字符,与在命令行文件扩展名例子中使用过的匹配模式类型相同,
即:
* 任意字符。
? 任意单字符。
[..] 类或范围中任意字符。
[root@localhost huangcd]# cat caseterm
#!/bin/bash
echo “choices are .. vt100,vt102,vt220”
echo -n “enter your terminal type:”
read TERMINAL
case $TERMINAL in
vt100|vt102) TERM=vt100
;;
vt220) TERM=vt220
;;
*) echo “`basename $0` : unknown response”>&2
echo “setting it to vt100 anyway,so there”
TERM=vt100
;;
esac
export TERM
echo “your terminal is set to $TERM”
[root@localhost huangcd]# sh caseterm
choices are .. vt100,vt102,vt220
enter your terminal type:vt100
your terminal is set to vt100
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列表可以包含替换、字符串和文件名,
[root@localhost huangcd]# cat forls
#!/bin/bash
for loop in `ls`
do
echo $loop
done
[root@localhost huangcd]# sh forls
addaccount.sh
append.sed
arraytest.awk
c
caseterm
change.sed
data.f
在f o r循环中省去i n列表选项时,它将接受命令行位置参数作为参数。实际上即指明:
for params in”$@”
或
for params in”$*”
下面的例子不使用i n列表选项, f o r循环查看特定参数$ @或$ *,以从命令行中取得参数。
[root@localhost huangcd]# sh forparam2 myfile1 myfile2 myfile3
you supplied myfile1 as a command line option
myfile1
you supplied myfile2 as a command line option
myfile2
you supplied myfile3 as a command line option
myfile3
[root@localhost huangcd]# cat forparam2
#!/bin/bash
for params
do
echo “you supplied $params as a command line option”
echo $params
done
下面的脚本包含i n”$ @”,结果与上面的脚本相同。
[root@localhost huangcd]# cat forparam2
#!/bin/bash
for params in “$@”
do
echo “you supplied $params as a command line option”
echo $params
done
对上述脚本采取进一步动作。如果要查看一系列文件,可在f o r循环里使用f i n d命令,利
用命令行参数,传递所有要查阅的文件。
脚本执行时,从命令行参数中取值并使用f i n d命令,这些取值形成- n a m e选项的参数值。
[root@localhost huangcd]# sh forfind passwd LPSO.AKSOP
/usr/lib/news/bin/auth/passwd
/usr/share/doc/nss_ldap-253/pam.d/passwd
[root@localhost huangcd]# cat forfind
for loop
do
find / -name $loop -print
done
匹配所有以L P S O开头文件并将其转换为大写。这里使用了l s和c a t命令。l s用于查询出相
关文件, c a t用于将之管道输出至t r命令。目标文件扩展名为.U C,注意在f o r循环中使用l s命令
时反引号的用法。
[root@localhost huangcd]# cat forUC
#!/bin/bash
for files in `ls LPSO*`
do
cat $files |tr “[a-z]” “[A-Z]” >$files.UC
done
下面的例子中, s e d用于删除所有空文件,并将输出导至以. H O L D . m v为扩展名的新文件
中,m v将这些文件移至初始文件中。
[root@localhost huangcd]# cat forsed
#!/bin/bash
for ifles in `ls LPSO*`
do
sed -e “/^$/d” $files>$files.HOLD
mv $files.HOLD $files
done