编写shell脚本--流控制:IF分支语句

5 篇文章 0 订阅


让我们来考虑一个使用伪代码表示地简单逻辑示例。伪代码是计算机语言的一种模拟,为的是方便人们理解。

X = 5
If X = 5, then:
	Say  "X equal 5."
Otherwise:
	Say "X is not equal to 5."

这就是一个分支的例子。根据条件,如果“X=5”,则表示为“X等于5”;否则,则说“X不等于5”。

一、使用if

通过shell,我们可以对上面地逻辑进行编码,如下所示:

x=5
if [ $x = 5 ]; then
	echo "x equals 5."
else
	echo "x does not equal 5."
fi

if语句地语法格式如下

if commands; then
	commands
elif commands; then
	commands
else
	commands
fi

二、退出状态

命令(包括我们编写地脚本和shell函数)在执行完毕后,会向操作系统发送一个值,称之为“退出状态”。这个值是0~255的整数,用来指示命令执行成功还是失败。按照惯例,数值0表示执行成功,其他地数值表示执行失败。shell提供了一个可以用来检测退出状态地参数。
在这里插入图片描述
在这个例子中,我们两次执行了ls命令。第一次,命令执行成功,如果显示参数“$?”的值,可以看到它是0。第二次执行ls命令时,产生了一个错误,再次显示参数“$?”地值,这次则为2,表示这个命令遇到了一个错误。有些命令使用不同的退出值来诊断错误,而许多命令在执行失败时,只是简单地退出并发送数字1。man手册中经常会包括一个标题为“Exit Status”的段落,它描述使用地代码。数字0总是表示执行成功。
shell提供了两个非常简单地内置命令,它们不做任何事情,除了以一个0或1退出状态来终止执行。“true”命令总是表示执行成功,而“false”命令总是表示执行失败。
在这里插入图片描述
我们可以用这两个命令来查看if语句是如果工作地。if语句真正做地事情是评估命令地成功和失败。
在这里插入图片描述
如果if后面有一系列地命令,那么则根据最后一个命令地执行结果进行评估。
在这里插入图片描述

三、使用test命令

目前为止,经常和if一起使用地命令是testtest命令会执行各种检查和比较。这个命令有两种等价地形式:

test expression

以及更流行的:

[ expression ]

这里的expression是一个表达式,其结果是true或false。当这个表达式为true时,test命令返回一个零退出状态,当这个表达式为false时,test命令的退出状态为1。

3.1、文件表达式

测试文件的表达式

表达式成为true的条件
file1 -ef file2file1和file2拥有相同的信息节点编号(这两个文件通过硬链接指向同一个文件)
file1 -nt file2file1比file2新
file1 -ot file2fil1比file2旧
-b filefile存在并且时一个块(设备)文件
-c filefile存在并且时一个字符(设备)文件
-d filefile存在并且是一个目录
-e filefile存在
-f filefile存在并且是一个普通文件
-g filefile存在并且设置了组ID
-G filefile存在并且属于有效组ID
-k filefile存在并且有“粘滞位(sticky bit)”属性
-L filefile存在并且是一个符号链接
-O filefile存在并且属于有效用户ID
-p filefile存在并且是一个命令管道
-r filefile存在并且可读(有效用户有可读权限)
-s filefile存在并且其长度大于0
-S filefile存在并且是一个网络套接字
-t fdfd是一个定向到终端/从终端定向的文件描述符,可以用来确定标准输入/输出/错误是否被重定向
-u filefile存在并且设置了setuid位
-w filefile存在并且可写(有效用户拥有可写权限)
-x filefile存在并且可执行(有效用户拥有可执行/搜索权限)
下面的脚本可用来演示某些文件表达式;
#!/bin/bash

#test-file: Evaluate the status of a file

FILE=~/.bashrc
if [ -e "$FILE" ]; then
			if [ -f "$FILE" ]; then
					echo "$FILE is a regolar file."
			fi
			if [ -d "$FILE" ]; then
					echo "$FILE is a directory."
			fi
			if [ -r "$FILE" ]; then
					echo "$FILE is readable."
			fi
			if [ -w "$FILE" ]; then
					echo "$FILE is writable."
			fi
			if [ -x "$FILE" ]; then
					echo "$FILE is execytable/searchable."
			fi
else
	echo "$FILE does not exist"
	exit 1
fi

exit

这个脚本会评估赋值给常量FILE的文件,并显示评估结果。关于该脚本,需要注意两个有趣的地方。首先,要注意$FILE在表达式内是怎么被引用的。尽管引号不是必需的,但是这可以防范参数为空的情况。如果$FILE的参数扩展产生一个空值,将导致一个错误(操作符会被解释为非空的字符串,而不是操作符)。用引号把参数括起来可以确保操作符后面总是跟随着一个字符串,即使字符串为空。其次,注意脚本末尾的exit命令。这个exit命令接受一个单独的可选参数,它敬爱嗯成为脚本的退出状态。当不传递参数时,退出状态默认为0。以这种方法使用exit命令,当$FILE扩展为一个不存在的文件名时,可以允许脚本提示失败。这个exit命令出现在脚本的最后一行。这样,当脚本执行到最后时,不管怎么样,默认情况下它将以退出状态零终止。
类似的,通过在return命令中包含一个整数参数,shell函数可以返回一个退出状态。如果要将上面的脚本转换为一个shell函数,从而能够在一个更大的程序中使用,可以将exit命令替换为return命令,并得到想要的行为。

test_file () {
		FILE=~/.bashrc
		if [ -e "$FILE" ]; then
						if [ -f "$FILE" ]; then
							echo "$FILE is a regolar file."
					fi
					if [ -d "$FILE" ]; then
							echo "$FILE is a directory."
					fi
					if [ -r "$FILE" ]; then
							echo "$FILE is readable."
					fi
					if [ -w "$FILE" ]; then
							echo "$FILE is writable."
					fi
					if [ -x "$FILE" ]; then
							echo "$FILE is execytable/searchable."
					fi
	else
					echo "$FILE does not exist"
					return 1
fi

)

3.2、字符串表达式

测试字符串表达式

表达式成为true的条件
stringstring不为i空
-n stringstring的长度大于0
-z stringstring的长度等于0
string1=string2string1和string2相等。单等号和双等号都可以使用,但是双等好使用的更多
string2==string1
string1!=string2string1和string2不相等
string1>string2在排序时,string1在string2之后
string1<string2在排序时,string1在string2之前

警告在使用test命令时,">“和”<"运算符必须用引号括起来(或者时使用反斜杠进行转义)。如果不这样做,就会被shell解释为重定向操作符,从而造成潜在的破坏性结果。同时注意,尽管bash文档中已经声明,排序遵从当前语系的排列规则,但并非如此。在bash 4.0版本以前(包括4.0版本),使用的是ASCLL(POSIX)排序方式。

下面是一个合并字符串表法式的脚本。

#!/bin/bash

#test-string: evaluate the value of a string

ANSWER=maybe

if [ -z "$ANSWER" ]; then
			echo "There is no answer." >&2
			exit 1
fi
if [ "$ANSWER" = "yes" ]; then
			echo "The answer is YES."
elif [ "$ANSWER" = "no" ]; then
			echo "The answer is NO."
elif [ "$ANSWER" = "maybe" ]; then
			echo "The answer is MAYBE."
else
			echo "The answer is UNKNOWN."
fi

在这个脚本中,我们评估了常量ANSWER。我们首先检查了这个字符串是否是空。如果是空,则终止脚本,并且把退出状态设置为1。注意应用到echo命令的重定向操作符i。它把错误信息"There is no answer."重定向到标准错误,这也是处理错误信息的“合理”方法。如果字符串非空,则评估这个字符串的值是否是“yes”、“no”或者“maybe”。我们使用elif(else if的缩写)来实现上述目的。通过使用elif,我们可以建立一个更复杂的逻辑测试。

3.3、整数表达式

整数判断操作

表达式成为true的条件
integer1 -eq integer2integer1和integer2相等
integer1 -ne integer2integer1和integer2不想等
integer1 -le integer2integer1小于等于integer2
integer1 -lt integer2integer1小于integer2
integer1 -ge integer1integer1大于等于integer2
integer1 -gt integer2integer1大于integer2
下面是一个演示这些表达式的脚本。
#!/bin/bash

#test-integer: evaluate the value of a integer:

INT=-5

if [ -z "$INT" ]; then
			echo "INT is empty." >&2
			exit 1
fi
if [ "$INT" -eq 0 ]; then
			echo "INT is zero."
else
			if [ $INT -lt 0]; then
					echo "INT is negative."
			else
					echo "INT is positive."
			fi
			if [ $((INT % 2)) -eq 0]; then
					echo "INT is even."
			else
					echo "INT is odd."
			fi
fi

这个脚本最有意思的地方是,如何判断一个整数是奇数还是偶数。通过用模数2进行求模运算,也就是将这个数值除以2并且返回余数,这就可以知道这个数值是奇数还是偶数了。

四、更现代地test命令版本

bash的最近版本包括了一个符号命令,它相当于增强的test命令。下面是这个命令的语法。

[ [expression ] ]

expression是一个表达式,其结果为true或false。[ [ ] ]命令和test命令类似(支持所有的表达式),不过增加了一个很重要的新字符串表达式。

string1=~regex

如果string1与扩展的正则表达式匹配,则返回true。这就为执行数据验证这样的任务提供了许多可能性。在前面整数表达式的例子中,如果常量INT含有整数意外的其他值,脚本就会执行失败。脚本需要有一种方法来验证常量包含的是否是整数。可以使用 [ [ ] ]和=~字符传表达式操作符,按照如下方式改进脚本:

#!/bin/bash

#test-integer: evaluate the value of a integer:

INT=-5
if [[ "$INT" =~ ^-[0-9]+$ ]]; then
			if [ "$INT" -eq 0 ]; then
						echo "INT is zero."
			else
						if [ $INT -lt 0]; then
								echo "INT is negative."
						else
								echo "INT is positive."
						fi
						if [ $((INT % 2)) -eq 0]; then
								echo "INT is even."
						else
								echo "INT is odd."
						fi
			fi
else
			echo "INT is not an integer." >&2
			exit1
fi

通过应用正则表达式,我们可以限制INT的值只能是字符串,而且字符串必须以减号(可选)开头,后面跟着一个或者多个数字。这个表达式同样消除了INT为空值的可能性。
[ [ ] ]增加的另外一个特性是==操作符支持模式匹配,就像路径名扩展那样。举例如下:

$ FILE = foo.bar
$ if [[ $FILE == foo.* ]]; then
> echo "$FILE matches pattern 'foo.*' "
> fi
foo.bar matches pattern 'foo.*'

这使得[[ ]]在评估文件和路径名的时候非常有用。

五、(())–为整数设计

除了[[]]的复合命令之外,bash同样提供了(())复合命令,它可用于操作整数。该命令支持一套完整的算术计算。
(())用于执行算术真值测试(arithmetic truth test)。当算术计算的结果是非零值时,则算术真值测试为true。
在这里插入图片描述
使用(()),我们可以简化test-integer2脚本,如下所示。

#!/bin/bash

#test-integer2a: evaluate the value of a integer:

INT=-5
if [[ "$INT" =~ ^-[0-9]+$ ]]; then
			if ( (INT == 0) ); then
						echo "INT is zero."
			else
						if ( (INT < 0) ); then
								echo "INT is negative."
						else
								echo "INT is positive."
						fi
						if (( ((INT %2 )) == 0 )); then
								echo "INT is even."
						else
								echo "INT is odd."
						fi
			fi
else
			echo "INT is not an integer." >&2
			exit1
fi

注意:这里使用了小于号、大于号和等号用来测试相等性。在处理整数时,这些语法看起来也更自然。另外,由于(())复合命令只是shell语法的一部分,而非普通命令,并且只能处理整数,所以它能够通过名字来识别变量,而且不需要执行扩展操作。

六、组合表达式

我们也可以将表达式组合起来,来创建更复杂的计算。表达式是使用逻辑运算符组合起来的。与test[[]]命令配套的逻辑运算符有所三个,它们是AND、OR和NOT。test和[[]]使用不同的操作符来表示这三种逻辑操作,如下表所示。

Operationtest[[]]and(())
AND-a&&
OR-o
NOT!!
下面是一个AND运算的例子。这个脚本用来检测一个整数是否属于某个范围内的值。
#!/bin/bash

#test-integer3:determine if an integer is within a

MIN_VAL=1
MAX_VAL=100

if [[ "$INT" = ~ ^-?[0-9]+$ ]]; then
			if [[ INT -ge MIN_VAL && INT -le MAX_VAL ]]; then
						echo "$INT is within $MIN_VAL to $MAX_VAL."
		 	else
		 				echo "$INT is out of range."
		 	fi
else
			echo "$INT is not an integer." >&2
			exit 1
fi

这个脚本中,检测整数INT的值是否在MIN_VAL和MAX_VAL之间。这是通过一个[[]]运算符来执行的,该运算符中包含两个用“&&”运算符分隔来的表达式。当然我们也可以使用test完成该功能。

if [ $INT -ge $MIN_VAL -a $INT -le $MAX_VAL ]; then
			echo "$INT is within $MIN_VAL to $MAX_VAL."
else
		 	echo "$INT is out of range."
fi

"!"否定运算符对表达式的运算结果取反,如果表达式为false,则返回true;反之,如果表达式为true,则返回false。在下面的脚本中,我们将修改判断逻辑,以判断INT的值是否在指定的范围之外:

#!/bin/bash

#test-integer3:determine if an integer is within a

MIN_VAL=1
MAX_VAL=100

if [[ "$INT" = ~ ^-?[0-9]+$ ]]; then
			if [[ !(INT -ge MIN_VAL && INT -le MAX_VAL) ]]; then
						echo "$INT is outside $MIN_VAL to $MAX_VAL."
		 	else
		 				echo "$INT is  in range."
		 	fi
else
			echo "$INT is not an integer." >&2
			exit 1
fi

当然我们也可以用圆括号把表达式括起来,以进行分组。如果不是用圆括号,“!”运算符仅对第一个表达式有效,而不是对两个组合后的表达式有效。用test命令可以按照如下进行编码:

if [ ! \($INT -ge $MIN_VAL -a $INT -le $MAX_VAL\) ]; then
			echo "$INT is outside $MIN_VAL to $MAX_VAL."
else
		 	echo "$INT is in range."
fi

由于test使用的所有表达式和操作符都被shell看做命令参数(不像[[]]以及(())),因此在bash中有特殊含义的字符,如"<“、”>“、”(“和”)",必须用引号括起来或者进行转义。
test[[]]命令基本上完成相同的功能,那么,哪一个更好呢?test更为传统(而且也是POSIX的一部分),而[[]]则是bash特定的。由于test命令的使用更为广泛,因此知道如何使用test更为重要。但是[[]]命令显然更有用,而且更容易编码。

七、控制运算符:另一种方式的分支

bash还提供了两种可以执行分支的控制运算符。“&&”(AND)和“||”(OR)运算符与[[]]复合命令中的逻辑运算符类似。语法如下:

command1 && command2

command1 || command2

理解这两个运算符是非常重要的。对于“&&”运算符来说,先执行command1,只有在command1执行成功时,command2才能够执行。对于“||”运算符来说,先执行command1,则只有在command1执行失败时,command2才能够执行。
从实用性考虑,这意味着可以这样做:

$ mkdir temp && cd temp

这会创建一个temp目录,并且当这个创建工作执行成功后,当前的工作目录此阿会更改为temp。只有在第一个mkdir命令执行成功后,才会尝试执行第二个命令。同样,看如下命令:

$ [-d temp] || mkdir temp

这个命令先检测temp目录是否存在,只有当检测失败时,才会创建这个目录。这个构造类型可以轻松处理脚本中的错误,例如:

$ [-d temp] || exit 1

如果脚本需要temp目录,而这个目录不存在,则这个脚本会终止,并且退出状态为1。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贝勒里恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值