兴海北路

---男儿仗剑自横行
<2008年3月>
2425262728291
2345678
9101112131415
16171819202122
23242526272829
303112345

统计

  • 随笔 - 85
  • 文章 - 0
  • 评论 - 17
  • 引用 - 0

常用链接

留言簿(6)

随笔分类

随笔档案

收藏夹

全是知识啊

搜索

  •  

最新评论

阅读排行榜

评论排行榜

shell编程范例之布尔运算
上个礼拜介绍了Shell编程范例之数值运算,对Shell下基本数值运算方法做了简单的介绍,这周将一起探讨布尔运算,即如何操作“真假值”。
    在bash里有这样的常量(实际上是两个内置命令,在这里我们姑且这么认为,后面将介绍),即true和false,一个表示真,一个表示假。对它们可以 进行与、或、非运算等常规的逻辑运算,在这一节,我们除了讨论这些基本逻辑运算外,还将讨论SHELL编程中的条件测试和命令列表,并介绍它们和布尔运算 的关系。

1. 常规的布尔运算

这里主要介绍bash里头常规的逻辑运算,与、或、非。

1.1 概要示例:在shell下如何进行逻辑运算

Quote:

// 单独测试true和false,可以看出true是真值,false为假
$ if true;then echo "YES"; else echo "NO"; fi
YES
$ if false;then echo "YES"; else echo "NO"; fi
NO
// 与运算
$ if true && true;then echo "YES"; else echo "NO"; fi
YES
$ if true && false;then echo "YES"; else echo "NO"; fi
NO
$ if false && false;then echo "YES"; else echo "NO"; fi
NO
$ if false && true;then echo "YES"; else echo "NO"; fi
NO
// 或运算
$ if true || true;then echo "YES"; else echo "NO"; fi
YES
$ if true || false;then echo "YES"; else echo "NO"; fi
YES
$ if false || true;then echo "YES"; else echo "NO"; fi
YES
$ if false || false;then echo "YES"; else echo "NO"; fi
NO
// 非运算,即取反
$ if ! false;then echo "YES"; else echo "NO"; fi
YES
$ if ! true;then echo "YES"; else echo "NO"; fi
NO



可以看出true和false按照我们对逻辑运算的理解进行着,但是为了能够更好的理解shell对逻辑运算的实现,我们还得弄清楚,true和false是怎么工作的?

1.2 范例演示:bash里头的true和false是我们通常认为的1和0么?

回答是:否。

Quote:

// true和false它们本身并非逻辑值,它们是shell内置命令,返回了“逻辑值”
$ true
$ echo $?
0
$ false
$ echo $?
1
// 看看true和false帮助和类型
$ help true false
true: true
     Return a successful result.
false: false
     Return an unsuccessful result.
$ type true false
true is a shell builtin
false is a shell builtin



说明:$?是一个特殊的变量,存放有上一个程序的结束状态(退出状态码)。

从 上面的操作不难联想到在C语言程序设计中为什么会强调在main函数前面加上int,并在末尾加上return 0。因为在shell里头,将把0作为程序是否成功结束的标志,这就是shell里头true和false的实质,它们用以反应某个程序是否正确结束,而 并非传统的真假值(1和0),相反的,它们返回的是0和1。不过庆幸的是,我们在做逻辑运算时,无须关心这些。

2. 条件测试

从上一节中,我们已经清楚的了解了shell下的“逻辑值”是什么:是程序结束后的返回值,如果成功返回,则为真,如果不成功返回,则为假。

而条件测试正好使用了test这么一个指令,它用来进行数值测试(各种数值属性测试)、字符串测试(各种字符串属性测试)、文件测试(各种文件属性测试),我们通过判断对应的测试是否成功,从而完成各种常规工作,在加上各种测试的逻辑组合后,将可以完成更复杂的工作。

2.1 概要示例:条件测试基本使用和各种测试的逻辑组合

Quote:

// 数值测试
$ if test 5 -eq 5;then echo "YES"; else echo "NO"; fi
YES
$ if test 5 -ne 5;then echo "YES"; else echo "NO"; fi
NO
// 字符串测试
$ if test -n "not empty";then echo "YES"; else echo "NO"; fi
YES
$ if test -z "not empty";then echo "YES"; else echo "NO"; fi
NO
$ if test -z "";then echo "YES"; else echo "NO"; fi
YES
$ if test -n "";then echo "YES"; else echo "NO"; fi
NO
// 文件测试
$ if test -f /boot/System.map; then echo "YES"; else echo "NO"; fi
YES
$ if test -d /boot/System.map; then echo "YES"; else echo "NO"; fi
NO
// 各种测试的组合
// 如果a,b,c都等于下面对应的值,那么打印YES,这里通过-a进行"与"测试
$ a=5;b=4;c=6;
$ if test $a -eq 5 -a $b -eq 4 -a $c -eq 6; then echo "YES"; else echo "NO"; fi
YES
// 测试某个“东西”是文件或者目录,这里通过-o进行“或”运算
$ if test -f /etc/profile -o -d /etc/profile;then echo "YES"; else echo "NO"; fi
YES
// 测试非运算
$ if test ! -f /etc/profile; then echo "YES"; else echo "NO"; fi
NO



上 面仅仅演示了test命令一些非常简单的测试,你可以通过help test获取test的更多使用方法。在这里需要注意的是,test命令内部的逻辑运算和shell的逻辑运算符有一些区别,对应的为-a和& &,-o与||,这两者不能混淆使用。而非运算都是!,下面对它们进行比较。

2.2 范例演示:-a与&&, -o与||,!与!

Quote:

// 要求某个文件有可执行权限并且有内容,用-a和&&分别实现
$ vim test.sh
$ cat test.sh
#!/bin/bash

echo "test"
$ chmod +x test.sh
$ if test -s test.sh -a -x test.sh; then echo "YES"; else echo "NO"; fi
YES
$ if test -s test.sh && test -x test.sh; then echo "YES"; else echo "NO"; fi
YES
// 要求某个字符串要么为空,要么和某个字符串相等
$ str1="test"
$ str2="test"
$ if test -z "$str2" -o "$str2" == "$str1"; then echo "YES"; else echo "NO"; fi
YES
$ if test -z "$str2" || test "$str2" == "$str1"; then echo "YES"; else echo "NO"; fi
YES
// 测试某个数字不满足指定的所有条件
$ i=5
$ if test ! $i -lt 5 -a $i -ne 6; then echo "YES"; else echo "NO"; fi
YES
$ if ! test $i -lt 5 -a $i -eq 6; then echo "YES"; else echo "NO"; fi
YES



很容易找出它们的区别,-a和-o使用在测试命令的内部,作为测试命令的参数,而&&和||是用来运算测试的返回值,!为两者通用。需要关注的是:
1)有时候我们可以不用!运算符,比如-eq和-ne刚好是相反的,用来测试两个数值是否相等;-z与-n也是对应的,用来测试某个字符串是否为空。
2)在bash里,test命令可以用[  ]运算符取代,但是需要注意,[之后与]之前需要加上额外的空格。
3)在测试字符串的时候,所有变量建议用双引号包含起来,以防止变量内容为空的时候出现仅有测试参数,没有测试内容的情况。

下面我们用实例来演示上面三个注意事项:
Quote:

// -ne和-eq对应的,我们有时候可以免去!运算
$ i=5
$ if test $i -eq 5; then echo "YES"; else echo "NO"; fi
YES
$ if test $i -ne 5; then echo "YES"; else echo "NO"; fi
NO
$ if test ! $i -eq 5; then echo "YES"; else echo "NO"; fi
NO
// 用[  ]可以取代test,这样看上去会“美观”很多
$ if [ $i -eq 5 ]; then echo "YES"; else echo "NO"; fi
YES
$ if [ $i -gt 4 ] && [ $i -lt 6 ]; then echo "YES"; else echo "NO"; fi
YES
// 记得给一些字符串变量加上"",记得[之后与]之前多加一个空格
$ str=""
$ if [ "$str" = "test"]; then echo "YES"; else echo "NO"; fi
-bash: [: missing `]'
NO
$ if [ $str = "test" ]; then echo "YES"; else echo "NO"; fi
-bash: [: =: unary operator expected
NO
$ if [ "$str" = "test" ]; then echo "YES"; else echo "NO"; fi
NO



到 这里,条件测试就介绍完了,下面我们将介绍“命令列表”,实际上在上面我们似乎已经使用过了,即多个test命令的组合,通过&&,|| 和!组合起来的命令序列。这个命令序列可以有效替换if/then的条件分支结构。这不难想到我们在C语言程序设计中经常做的如下的选择题(很无聊的例 子,但是有意义):下面是否会打印j,如果打印,将打印什么?



Code:

[Ctrl+A Select All]



很 容易知道将打印数字5,因为i==5这个条件成立,而且随后是&&,要判断整个条件是否成立,我们得进行后面的判断,可是这个判断并非常 规的判断,而是先把j修改为5,再转换为真值,所以条件为真,打印出5。因此,这句可以解释为:如果i等于5,那么把j赋值为5,如果j大于1(因为之前 已经为真),那么打印出j的值。这样用&&连结起来的判断语句替代了两个if条件分支语句。

正是基于逻辑运算特有的性质,我们可以通过&&,||来取代if/then等条件分支结构,这样就产生了命令列表。

3. 命令列表

3.1 概要示例:命令列表的执行规律

命令列表的执行规律符合逻辑运算的运算规律,用&&连接起来的命令,如果前者成功返回,将执行后面的命令,反之不然;用||连接起来的命令,如果前者成功返回,将不执行后续命令,反之不然。

Quote:

// 如果ping通www.lzu.edu.cn,那么打印连通信息
$ ping -c 1 www.lzu.edu.cn -W 1 && echo "=======connected======="
// 这种情况下,只执行第一条命令
$ echo "iiii" || echo "jjjj"
iiii



非常有趣的问题出来了,即我们上面已经提到的:为什么要让C程序在main函数的最后返回0?如果不这样,把这种程序放入命令列表会有什么样的结果?你自己写个简单的C程序看看,然后放入命令列表看看。

3.2 范例演示:命令列表的作用

在有些时候取代if/then等条件分支结构,这样可以省略一些代码,而且使得程序比较美观、易读,例如:

在脚本里判断程序的参数个数,和参数类型


Code:

[Ctrl+A Select All]


上例要求参数个数为1并且类型为数字。

再加上exit 1,我们将省掉if/then结构


Code:

[Ctrl+A Select All]



这样处理后,对程序参数的判断仅仅需要简单的一行代码,而且变得更美观。

4. 总结

这一节介绍了shell编程中的逻辑运算,条件测试和命令列表。但是貌似没介绍实用一点的范例,是不是得综合起来写一个,或者分析一个现成的程序呢?

还是写一个吧:获取某个ftp服务器根目录下文件信息?



Code:

[Ctrl+A Select All]



说明:

刚开始检查参数个数,至少需要一个,接着检查系统中是否安装有ftp,awk,iconv命令,然后处理参数,最后用ftp命令登录到服务器上下载根目录下的文件信息,最后分离出文件名,并处理编码。关于iconv的用法,请参考man iconv。

演示:

Quote:

$ chmod +x ftpls.sh
$ ./ftpls.sh mirror.lzu.edu.cn
about.html
cldp
doc
gentoo
gnu.org
index.html
kernel.org
knoppix
os
rfc
slackware
slind
software
tldp
tmp
ubuntu
ubuntu-cn
welcome.msg
$ ./ftpls.sh xxxy.lzu.edu.cn ftp ftp gb2312
incoming
pub
xxxy.lzu.edu.cn.url
管理员的信箱是xxxylzu@gmail.com
近期由于大量非校园网用户连接FTP,导致正常用户无法访问,故暂时关闭外网访问



再 看看上面的代码,对参数的判断没有用到一个条件分支语句,用命令列表就非常简单的实现了。另外,你是不是想到对这个代码进行改写呢?实现ftp匿名站点的 扫描,结合数据库和CGI实现一个简单的ftp搜索引擎?试试看。不过做这些工作实际上还有很多其他潜在的方法呢,比如用专门的扫描工具(比如nmap, hping之类),或者自己用C写一个(参考该blog里头的相关帖子)。

关于SHELL编程之布尔运算就到此结束了,欢迎您指出不足、提出意见。很多后续例子将直接回帖发布。

在下一节中,我们将讨论shell编程中非常有趣的字符串操作。

posted on 2008-03-14 15:43 随意门 阅读(5966) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理