第二部分:Shell编程(三)
二十一、Shell declare和typeset命令:设置变量属性
declare 和 typeset 都是 Shell 内建命令,它们的用法相同,都用来设置变量的属性。不过 typeset 已经被弃用了,建议使用 declare 代替。
declare 命令的用法如下所示:
declare [+/-] [aAfFgilprtux] [变量名=变量值]
其中,-
表示设置属性,+
表示取消属性,aAfFgilprtux
都是具体的选项,它们的含义如下表所示:
选项 | 含义 |
---|---|
-f [name] | 列出之前由用户在脚本中定义的函数名称和函数体。 |
-F [name] | 仅列出自定义函数名称。 |
-g name | 在 Shell 函数内部创建全局变量。 |
-p [name] | 显示指定变量的属性和值。 |
-a name | 声明变量为普通数组。 |
-A name | 声明变量为关联数组(支持索引下标为字符串)。 |
-i name | 将变量定义为整数型。 |
-r name[=value] | 将变量定义为只读(不可修改和删除),等价于 readonly name。 |
-x name[=value] | 将变量设置为环境变量,等价于 export name[=value]。 |
【实例1】将变量声明为整数并进行计算。
#!/bin/bash
declare -i m n ret #将多个变量声明为整数
m=10
n=30
ret=$m+$n
echo $ret
运行结果:
40
【实例2】将变量定义为只读变量。
[c.biancheng.net]$ declare -r n=10
[c.biancheng.net]$ n=20
bash: n: 只读变量
[c.biancheng.net]$ echo $n
10
【实例3】显示变量的属性和值。
[c.biancheng.net]$ declare -r n=10
[c.biancheng.net]$ declare -p n
declare -r n="10"
二十二、Shell数学计算(算术运算,加减乘除运算)
如果要执行算术运算(数学计算),就离不开各种运算符号,和其他编程语言类似,Shell 也有很多算术运算符,下面就给大家介绍一下常见的 Shell 算术运算符,如下表所示。
算术运算符 | 说明/含义 |
---|---|
+、- | 加法(或正号)、减法(或负号) |
*、/、% | 乘法、除法、取余(取模) |
** | 幂运算 |
++、-- | 自增和自减,可以放在变量的前面也可以放在变量的后面 |
!、&&、|| | 逻辑非(取反)、逻辑与(and)、逻辑或(or) |
<、<=、>、>= | 比较符号(小于、小于等于、大于、大于等于) |
==、!=、= | 比较符号(相等、不相等;对于字符串,= 也可以表示相当于) |
<<、>> | 向左移位、向右移位 |
~、|、 &、^ | 按位取反、按位或、按位与、按位异或 |
=、+=、-=、*=、/=、%= | 赋值运算符,例如 a+=1 相当于 a=a+1,a-=1 相当于 a=a-1 |
但是,Shell 和其它编程语言不同,Shell 不能直接进行算数运算,必须使用数学计算命令,这让初学者感觉很困惑,也让有经验的程序员感觉很奇葩。
下面我们先来看一个反面的例子:
[c.biancheng.net]$ echo 2+8
2+8
[c.biancheng.net]$ a=23
[c.biancheng.net]$ b=$a+55
[c.biancheng.net]$ echo $b
23+55
[c.biancheng.net]$ b=90
[c.biancheng.net]$ c=$a+$b
[c.biancheng.net]$ echo $c
23+90
从上面的运算结果可以看出,默认情况下,Shell 不会直接进行算术运算,而是把+
两边的数据(数值或者变量)当做字符串,把+
当做字符串连接符,最终的结果是把两个字符串拼接在一起形成一个新的字符串。
这是因为,在 Bash Shell 中,如果不特别指明,每一个变量的值都是字符串,无论你给变量赋值时有没有使用引号,值都会以字符串的形式存储。
换句话说,Bash shell 在默认情况下不会区分变量类型,即使你将整数和小数赋值给变量,它们也会被视为字符串,这一点和大部分的编程语言不同。
数学计算命令
要想让数学计算发挥作用,必须使用数学计算命令,Shell 中常用的数学计算命令如下表所示。
运算操作符/运算命令 | 说明 |
---|---|
(()) | 用于整数运算,效率很高,推荐使用。 |
let | 用于整数运算,和 (()) 类似。 |
$[] | 用于整数运算,不如 (()) 灵活。 |
expr | 可用于整数运算,也可以处理字符串。比较麻烦,需要注意各种细节,不推荐使用。 |
bc | Linux下的一个计算器程序,可以处理整数和小数。Shell 本身只支持整数运算,想计算小数就得使用 bc 这个外部的计算器。 |
declare -i | 将变量定义为整数,然后再进行数学运算时就不会被当做字符串了。功能有限,仅支持最基本的数学运算(加减乘除和取余),不支持逻辑运算、自增自减等,所以在实际开发中很少使用。 |
如果大家时间有限,只学习 (()) 和 bc 即可,不用学习其它的了: (()) 可以用于整数计算,bc 可以小数计算。
在接下来的章节中,我们将逐一为大家讲解 Shell 中的各种运算符号及运算命令。
二十三、Shell (()):对整数进行数学运算
双小括号 (( )) 是 Bash Shell 中专门用来进行整数运算的命令,它的效率很高,写法灵活,是企业运维中常用的运算命令。
注意:(( )) 只能进行整数运算,不能对小数(浮点数)或者字符串进行运算。后续讲到的 bc 命令可以用于小数运算。
1、Shell (( )) 的用法
双小括号 (( )) 的语法格式为:
((表达式))
通俗地讲,就是将数学运算表达式放在((
和))
之间。
表达式可以只有一个,也可以有多个,多个表达式之间以逗号,
分隔。对于多个表达式的情况,以最后一个表达式的值作为整个 (( )) 命令的执行结果。
可以使用$
获取 (( )) 命令的结果,这和使用$
获得变量值是类似的。
运算操作符/运算命令 | 说明 |
---|---|
((a=10+66) ((b=a-15)) ((c=a+b)) | 这种写法可以在计算完成后给变量赋值。以 ((b=a-15)) 为例,即将 a-15 的运算结果赋值给变量 c。 注意,使用变量时不用加 $ 前缀,(( )) 会自动解析变量名。 |
a=$((10+66) b=$((a-15)) c=$((a+b)) | 可以在 (( )) 前面加上$ 符号获取 (( )) 命令的执行结果,也即获取整个表达式的值。以 c=$((a+b)) 为例,即将 a+b 这个表达式的运算结果赋值给变量 c。注意,类似 c=((a+b)) 这样的写法是错误的,不加 $ 就不能取得表达式的结果。 |
((a>7 && b==c)) | (( )) 也可以进行逻辑运算,在 if 语句中常会使用逻辑运算。 |
echo $((a+10)) | 需要立即输出表达式的运算结果时,可以在 (( )) 前面加$ 符号。 |
((a=3+5, b=a+10)) | 对多个表达式同时进行计算。 |
在 (( )) 中使用变量无需加上$
前缀,(( )) 会自动解析变量名,这使得代码更加简洁,也符合程序员的书写习惯。
2、Shell (( )) 实例演示
【实例1】利用 (( )) 进行简单的数值计算。
[c.biancheng.net]$ echo $((1+1))
2
[c.biancheng.net]$ echo $((6-3))
3
[c.biancheng.net]$ i=5
[c.biancheng.net]$ ((i=i*2)) #可以简写为 ((i*=2))。
[c.biancheng.net]$ echo $i #使用 echo 输出变量结果时要加 $。
10
【实例2】用 (( )) 进行稍微复杂一些的综合算术运算。
[c.biancheng.net]$ ((a=1+2**3-4%3))
[c.biancheng.net]$ echo $a
8
[c.biancheng.net]$ b=$((1+2**3-4%3)) #运算后将结果赋值给变量,变量放在了括号的外面。
[c.biancheng.net]$ echo $b
8
[c.biancheng.net]$ echo $((1+2**3-4%3)) #也可以直接将表达式的结果输出,注意不要丢掉 $ 符号。
8
[c.biancheng.net]$ a=$((100*(100+1)/2)) #利用公式计算1+2+3+...+100的和。 [c.biancheng.net]$ echo $a
5050
[c.biancheng.net]$ echo $((100*(100+1)/2)) #也可以直接输出表达式的结果。
5050
【实例3】利用 (( )) 进行逻辑运算。
[c.biancheng.net]$ echo $((3<8)) #3<8 的结果是成立的,因此,输出了 1,1 表示真
1
[c.biancheng.net]$ echo $((8<3)) #8<3 的结果是不成立的,因此,输出了 0,0 表示假。
0
[c.biancheng.net]$ echo $((8==8)) #判断是否相等。
1
[c.biancheng.net]$ if ((8>7&&5==5))
> then
> echo yes
> fi
yes
最后是一个简单的 if 语句的格式,它的意思是,如果 8>7 成立,并且 5==5 成立,那么输出 yes。显然,这两个条件都是成立的,所以输出了 yes。
【实例4】利用 (( )) 进行自增(++)和自减(--)运算。
[c.biancheng.net]$ a=10
[c.biancheng.net]$ echo $((a++)) #如果++在a的后面,那么在输出整个表达式时,会输出a的值,因为a为10,所以表达式的值为10。
10
[c.biancheng.net]$ echo $a #执行上面的表达式后,因为有a++,因此a会自增1,因此输出a的值为11。
11
[c.biancheng.net]$ a=11
[c.biancheng.net]$ echo $((a--)) #如果--在a的后面,那么在输出整个表达式时,会输出a的值,因为a为11,所以表达式的值的为11。
11
[c.biancheng.net]$ echo $a #执行上面的表达式后,因为有a--,因此a会自动减1,因此a为10。
10
[c.biancheng.net]$ a=10
[c.biancheng.net]$ echo $((--a)) #如果--在a的前面,那么在输出整个表达式时,先进行自增或自减计算,因为a为10,且要自减,所以表达式的值为9。
9
[c.biancheng.net]$ echo $a #执行上面的表达式后,a自减1,因此a为9。
9
[c.biancheng.net]$ echo $((++a)) #如果++在a的前面,输出整个表达式时,先进行自增或自减计算,因为a为9,且要自增1,所以输出10。 10 [c.biancheng.net]$ echo $a #执行上面的表达式后,a自增1,因此a为10。
10
本教程假设读者具备基本的编程能力,相信读者对于前自增(前自减)和后自增(后自减)的区别也非常清楚,这里就不再赘述,只进行简单的说明:
- 执行 echo $((a++)) 和 echo $((a--)) 命令输出整个表达式时,输出的值即为 a 的值,表达式执行完毕后,会再对 a 进行 ++、-- 的运算;
- 而执行 echo $((++a)) 和 echo $((--a)) 命令输出整个表达式时,会先对 a 进行 ++、-- 的运算,然后再输出表达式的值,即为 a 运算后的值。
【实例5】利用 (( )) 同时对多个表达式进行计算。
[c.biancheng.net]$ ((a=3+5, b=a+10)) #先计算第一个表达式,再计算第二个表达式 [c.biancheng.net]$ echo $a $b
8 18
[c.biancheng.net]$ c=$((4+8, a+b)) #以最后一个表达式的结果作为整个(())命令的执行结果 [c.biancheng.net]$ echo $c
26
二十四、Shell let命令:对整数进行数学运算
let 命令和双小括号 (( )) 的用法是类似的,它们都是用来对整数进行运算。
注意:和双小括号 (( )) 一样,let 命令也只能进行整数运算,不能对小数(浮点数)或者字符串进行运算。
Shell let 命令的语法格式为:
let 表达式
或者
let "表达式"
或者
let '表达式'
它们都等价于((表达式))
。
当表达式中含有 Shell 特殊字符(例如 |)时,需要用双引号" "
或者单引号' '
将表达式包围起来。
和 (( )) 类似,let 命令也支持一次性计算多个表达式,并且以最后一个表达式的值作为整个 let 命令的执行结果。但是,对于多个表达式之间的分隔符,let 和 (( )) 是有区别的:
- let 命令以空格来分隔多个表达式;
- (( )) 以逗号
,
来分隔多个表达式。
另外还要注意,对于类似let x+y
这样的写法,Shell 虽然计算了 x+y 的值,但却将结果丢弃;若不想这样,可以使用let sum=x+y
将 x+y 的结果保存在变量 sum 中。
这种情况下 (( )) 显然更加灵活,可以使用$((x+y))
来获取 x+y 的结果。请看下面的例子:
[c.biancheng.net]$ a=10 b=20
[c.biancheng.net]$ echo $((a+b))
30
[c.biancheng.net]$ echo let a+b #错误,echo会把 let a+b作为一个字符串输出
let a+b
Shell let 命令实例演示
【实例1】给变量 i 加 8:
[c.biancheng.net]$ i=2
[c.biancheng.net]$ let i+=8
[c.biancheng.net]$ echo $i
10
let i+=8 等同于 ((i+=8)),但后者效率更高。
【实例2】let 后面可以跟多个表达式。
[c.biancheng.net]$ a=10 b=35
[c.biancheng.net]$ let a+=6 c=a+b #多个表达式以空格为分隔
[c.biancheng.net]$ echo $a $c
16 51
二十五、Shell $[]:对整数进行数学运算
和 (())、let 命令类似,$[] 也只能进行整数运算。
Shell $[] 的用法如下:
$[表达式]
$[] 会对表达式
进行计算,并取得计算结果。如果表达式
中包含了变量,那么你可以加$
,也可以不加。
Shell $[] 举例:
[c.biancheng.net]$ echo $[3*5] #直接输出结算结果
15
[c.biancheng.net]$ echo $[(3+4)*5] #使用()
35
[c.biancheng.net]$ n=6
[c.biancheng.net]$ m=$[n*2] #将计算结果赋值给变量
[c.biancheng.net]$ echo $[m+n]
18
[c.biancheng.net]$ echo $[$m*$n] #在变量前边加$也是可以的
72
[c.biancheng.net]$ echo $[4*(m+n)]
72
需要注意的是,不能单独使用 $[],必须能够接收 $[] 的计算结果。例如,下面的用法是错误的:
[c.biancheng.net]$ $[3+4]
bash: 7: 未找到命令...
[c.biancheng.net]$ $[m+3]
bash: 15: 未找到命令...
二十六、Shell expr命令:对整数进行运算
expr 是 evaluate expressions 的缩写,译为“表达式求值”。Shell expr 是一个功能强大,并且比较复杂的命令,它除了可以实现整数计算,还可以结合一些选项对字符串进行处理,例如计算字符串长度、字符串比较、字符串匹配、字符串提取等。
本节只讲解 expr 在整数计算方面的应用,并不涉及字符串处理,有兴趣的读者请自行研究。
Shell expr 对于整数计算的用法为:
expr 表达式
expr 对表达式
的格式有几点特殊的要求:
- 出现在
表达式
中的运算符、数字、变量和小括号的左右两边至少要有一个空格,否则会报错。 - 有些特殊符号必须用反斜杠
\
进行转义(屏蔽其特殊含义),比如乘号*
和小括号()
,如果不用\
转义,那么 Shell 会把它们误解为正则表达式中的符号(*
对应通配符,()
对应分组)。 - 使用变量时要加
$
前缀。
【实例1】expr 整数计算简单举例:
[c.biancheng.net]$ expr 2 +3 #错误:加号和 3 之前没有空格
expr: 语法错误
[c.biancheng.net]$ expr 2 + 3 #这样才是正确的
5
[c.biancheng.net]$ expr 4 * 5 #错误:乘号没有转义
expr: 语法错误
[c.biancheng.net]$ expr 4 \* 5 #使用 \ 转义后才是正确的
20
[c.biancheng.net]$ expr ( 2 + 3 ) \* 4 #小括号也需要转义
bash: 未预期的符号 `2' 附近有语法错误
[c.biancheng.net]$ expr \( 2 + 3 \) \* 4 #使用 \ 转义后才是正确的
20
[c.biancheng.net]$ n=3
[c.biancheng.net]$ expr n + 2 #使用变量时要加 $
expr: 非整数参数
[c.biancheng.net]$ expr $n + 2 #加上 $ 才是正确的
5
[c.biancheng.net]$ m=7
[c.biancheng.net]$ expr $m \* \( $n + 5 \)
56
以上是直接使用 expr 命令,计算结果会直接输出,如果你希望将计算结果赋值给变量,那么需要将整个表达式用反引号``
(位于 Tab 键的上方)包围起来,请看下面的例子。
【实例2】将 expr 的计算结果赋值给变量:
[c.biancheng.net]$ m=5
[c.biancheng.net]$ n=`expr $m + 10`
[c.biancheng.net]$ echo $n
15
你看,使用 expr 进行数学计算是多么的麻烦呀,需要注意各种细节,我奉劝大家还是省省心,老老实实用 (())、let 或者 $[] 吧。
二十七、Linux bc命令:一款数学计算器
Bash Shell 内置了对整数运算的支持,但是并不支持浮点运算,而 Linux bc 命令可以很方便的进行浮点运算,当然整数运算也不再话下。
bc 甚至可以称得上是一种编程语言了,它支持变量、数组、输入输出、分支结构、循环结构、函数等基本的编程元素,所以 Linux 手册中是这样来描述 bc 的:
An arbitrary precision calculator language
翻译过来就是“一个任意精度的计算器语言”。
在终端输入bc
命令,然后回车即可进入 bc 进行交互式的数学计算。在 Shell 编程中,我们也可以通过管道和输入重定向来使用 bc。
本节我们先学习如何在交互式环境下使用 bc,然后再学习如何在 Shell 编程中使用 bc,这样就易如反掌了。
1、从终端进入 bc
在终端输入 bc 命令,然后回车,就可以进入 bc,请看下图:
bc 命令还有一些选项,可能你会用到,请看下表。
选项 | 说明 |
---|---|
-h | --help | 帮助信息 |
-v | --version | 显示命令版本信息 |
-l | --mathlib | 使用标准数学库 |
-i | --interactive | 强制交互 |
-w | --warn | 显示 POSIX 的警告信息 |
-s | --standard | 使用 POSIX 标准来处理 |
-q | --quiet | 不显示欢迎信息 |
例如你不想输入 bc 命令后显示一堆没用的信息,那么可以输入bc -q
:
2、在交互式环境下使用 bc
使用 bc 进行数学计算是非常容易的,像平常一样输入数学表达式,然后按下回车键就可以看到结果,请看下图。
值得一提的是,我们定义了一个变量 n,然后在计算中也使用了 n,可见 bc 是支持变量的。
除了变量,bc 还支持函数、循环结构、分支结构等常见的编程元素,它们和其它编程语言的语法类似。下面我们定义一个求阶乘的函数:
其实我们很少使用这么复杂的功能,大部分情况下还是把 bc 作为普通的数学计算器,求一下表达式的值而已,所以大家不必深究,了解一下即可。
(1)内置变量
bc 有四个内置变量,我们在计算时会经常用到,如下表所示:
变量名 | 作 用 |
---|---|
scale | 指定精度,也即小数点后的位数;默认为 0,也即不使用小数部分。 |
ibase | 指定输入的数字的进制,默认为十进制。 |
obase | 指定输出的数字的进制,默认为十进制。 |
last 或者 . | 表示最近打印的数字 |
【实例1】scale 变量用法举例:
刚开始的时候,10/3 的值为 3,不带小数部分,就是因为 scale 变量的默认值为 0;后边给 scale 指定了一个大于 0 的值,就能看到小数部分了。
【实例2】ibase 和 obase 变量用法举例:
注意:obase 要尽量放在 ibase 前面,因为 ibase 设置后,后面的数字都是以 ibase 的进制来换算的。
(2)内置函数
除了内置变量,bc 还有一些内置函数,如下表所示:
函数名 | 作用 |
---|---|
s(x) | 计算 x 的正弦值,x 是弧度值。 |
c(x) | 计算 x 的余弦值,x 是弧度值。 |
a(x) | 计算 x 的反正切值,返回弧度值。 |
l(x) | 计算 x 的自然对数。 |
e(x) | 求 e 的 x 次方。 |
j(n, x) | 贝塞尔函数,计算从 n 到 x 的阶数。 |
要想使用这些数学函数,在输入 bc 命令时需要使用-l
选项,表示启用数学库。请看下面的例子:
(3)在一行中使用多个表达式
在前边的例子中,我们基本上是一行一个表达式,这样看起来更加舒服;如果你愿意,也可以将多个表达式放在一行,只要用分号;
隔开就行。请看下面的例子:
3、在 Shell 中使用 bc 计算器
在 Shell 脚本中,我们可以借助管道或者输入重定向来使用 bc 计算器。
- 管道是 Linux 进程间的一种通信机制,它可以将前一个命令(进程)的输出作为下一个命令(进程)的输入,两个命令之间使用竖线
|
分隔。 - 通常情况下,一个命令从终端获得用户输入的内容,如果让它从其他地方(比如文件)获得输入,那么就需要重定向。
此处我们并不打算展开讲解管道和重定向,不了解的小伙伴请自行百度。
(1)借助管道使用 bc 计算器
如果读者希望直接输出 bc 的计算结果,那么可以使用下面的形式:
echo "expression" | bc
expression
就是希望计算的数学表达式,它必须符合 bc 的语法,上面我们已经进行了介绍。在 expression 中,还可以使用 Shell 脚本中的变量。
使用下面的形式可以将 bc 的计算结果赋值给 Shell 变量:
variable=$(echo "expression" | bc)
variable 就是变量名。
【实例1】最简单的形式:
[c.biancheng.net]$ echo "3*8"|bc
24
[c.biancheng.net]$ ret=$(echo "4+9"|bc)
[c.biancheng.net]$ echo $ret
13
【实例2】使用 bc 中的变量:
[c.biancheng.net]$ echo "scale=4;3*8/7"|bc
3.4285
[c.biancheng.net]$ echo "scale=4;3*8/7;last*5"|bc
3.4285 17.1425
【实例3】使用 Shell 脚本中的变量:
[c.biancheng.net]$ x=4
[c.biancheng.net]$ echo "scale=5;n=$x+2;e(n)"|bc -l
403.42879
在第二条命令中,$x
表示使用第一条 Shell 命令中定义的变量,n
是在 bc 中定义的新变量,它和 Shell 脚本是没关系的。
【实例4】进制转换:
#十进制转十六进制
[mozhiyan@localhost ~]$ m=31
[mozhiyan@localhost ~]$ n=$(echo "obase=16;$m"|bc)
[mozhiyan@localhost ~]$ echo $n 1F #十六进制转十进制
[mozhiyan@localhost ~]$ m=1E
[mozhiyan@localhost ~]$ n=$(echo "obase=10;ibase=16;$m"|bc)
[mozhiyan@localhost ~]$ echo $n
30
(2)借助输入重定向使用 bc 计算器
可以使用下面的形式将 bc 的计算结果赋值给 Shell 变量:
variable=$(bc << EOF
expressions
EOF
)
其中,variable
是 Shell 变量名,express
是要计算的数学表达式(可以换行,和进入 bc 以后的书写形式一样),EOF
是数学表达式的开始和结束标识(你也可以换成其它的名字,比如 aaa、bbb 等)。
请看下面的例子:
[c.biancheng.net]$ m=1E
[c.biancheng.net]$ n=$(bc << EOF
> obase=10;
> ibase=16;
> print $m
> EOF
> )
[c.biancheng.net]$ echo $n
30
如果你有大量的数学计算,那么使用输入重定向就比较方便,因为数学表达式可以换行,写起来更加清晰明了。
二十八、Shell declare -i:将变量声明为整数类型
在《二十一、Shell declare和typeset命令:设置变量属性》一节中,我们已经讲解了 declare 命令的各种选项,为了让 Shell 进行整数运算,本节我们重点讲解-i
选项。
默认情况下,Shell 中每一个变量的值都是字符串(不了解的读者请猛击《一、Shell变量》),即使你给变量赋值一个数字,它其实也是字符串,所以在进行数学计算时会出错。
使用 declare 命令的-i
选项可以将一个变量声明为整数类型,这样在进行数学计算时就不会作为字符串处理了,请看下面的例子:
#!/bin/bash
declare -i m n ret
m=10
n=30
ret=$m+$n
echo $ret
ret=$n/$m
echo $ret
运行结果:
40
3
除了将 m、n 定义为整数,还必须将 ret 定义为整数,如果不这样做,在执行ret=$m+$n
和ret=$n/$m
时,Shell 依然会将 m、n 视为字符串。
此外,你也不能写类似echo $m+$n
这样的语句,这种情况下 m、n 也会被视为字符串。
总之,除了将参与运算的变量定义为整数,还得将承载结果的变量定义为整数,而且只能用整数类型的变量来承载运算结果,不能直接使用 echo 输出。
和 (())、let、$[] 不同,declare -i
的功能非常有限,仅支持最基本的数学运算(加减乘除和取余),不支持逻辑运算(比较运算、与运算、或运算、非运算),所以在实际开发中很少使用。
二十九、Shell if else语句
和其它编程语言类似,Shell 也支持选择结构,并且有两种形式,分别是 if else 语句和 case in 语句。本节我们先介绍 if else 语句,case in 语句将会在后面介绍。
如果你已经熟悉了C语言、Java、JavaScript等其它编程语言,那么你可能会觉得 Shell 中的 if else 语句有点奇怪。
1、if 语句
最简单的用法就是只使用 if 语句,它的语法格式为:
if condition
then
statement(s)
fi
condition
是判断条件,如果 condition 成立(返回“真”),那么 then 后边的语句将会被执行;如果 condition 不成立(返回“假”),那么不会执行任何语句。
从本质上讲,if 检测的是命令的退出状态,我们将在下节中深入讲解。
注意,最后必须以fi
来闭合,fi 就是 if 倒过来拼写。也正是有了 fi 来结尾,所以即使有多条语句也不需要用{ }
包围起来。
如果你喜欢,也可以将 then 和 if 写在一行:
if condition; then
statement(s)
fi
请注意 condition 后边的分号;
,当 if 和 then 位于同一行的时候,这个分号是必须的,否则会有语法错误。
(1)实例1
下面的例子使用 if 语句来比较两个数字的大小:
#!/bin/bash
read a
read b
if (( $a == $b ))
then
echo "a和b相等"
fi
运行结果:
84↙
84↙
a和b相等
在《二十三、Shell (()):对整数进行数学运算》一节中我们讲到,(())
是一种数学计算命令,它除了可以进行最基本的加减乘除运算,还可以进行大于、小于、等于等关系运算,以及与、或、非逻辑运算。当 a 和 b 相等时,(( $a == $b ))
判断条件成立,进入 if,执行 then 后边的 echo 语句。
(2)实例2
在判断条件中也可以使用逻辑运算符,例如:
#!/bin/bash
read age
read iq
if (( $age > 18 && $iq < 60 ))
then
echo "你都成年了,智商怎么还不及格!"
echo "来C语言中文网(http://c.biancheng.net/)学习编程吧,能迅速提高你的智商。"
fi
运行结果:
20↙
56↙
你都成年了,智商怎么还不及格!
来C语言中文网(http://c.biancheng.net/)学习编程吧,能迅速提高你的智商。
&&
就是逻辑“与”运算符,只有当&&
两侧的判断条件都为“真”时,整个判断条件才为“真”。
熟悉其他编程语言的读者请注意,即使 then 后边有多条语句,也不需要用{ }
包围起来,因为有 fi 收尾呢。
2、if else 语句
如果有两个分支,就可以使用 if else 语句,它的格式为:
if condition
then
statement1
else
statement2
fi
如果 condition 成立,那么 then 后边的 statement1 语句将会被执行;否则,执行 else 后边的 statement2 语句。
举个例子:
#!/bin/bash
read a
read b
if (( $a == $b ))
then
echo "a和b相等"
else
echo "a和b不相等,输入错误"
fi
运行结果:
10↙
20↙
a 和 b 不相等,输入错误
从运行结果可以看出,a 和 b 不相等,判断条件不成立,所以执行了 else 后边的语句。
3、if elif else 语句
Shell 支持任意数目的分支,当分支比较多时,可以使用 if elif else 结构,它的格式为:
if condition1
then
statement1
elif condition2
then
statement2
elif condition3
then
statement3
……
else
statementn
fi
注意,if 和 elif 后边都得跟着 then。
整条语句的执行逻辑为:
- 如果 condition1 成立,那么就执行 if 后边的 statement1;如果 condition1 不成立,那么继续执行 elif,判断 condition2。
- 如果 condition2 成立,那么就执行 statement2;如果 condition2 不成立,那么继续执行后边的 elif,判断 condition3。
- 如果 condition3 成立,那么就执行 statement3;如果 condition3 不成立,那么继续执行后边的 elif。
- 如果所有的 if 和 elif 判断都不成立,就进入最后的 else,执行 statementn。
举个例子,输入年龄,输出对应的人生阶段:
#!/bin/bash
read age
if (( $age <= 2 )); then
echo "婴儿"
elif (( $age >= 3 && $age <= 8 )); then
echo "幼儿"
elif (( $age >= 9 && $age <= 17 )); then
echo "少年"
elif (( $age >= 18 && $age <=25 )); then
echo "成年"
elif (( $age >= 26 && $age <= 40 )); then
echo "青年"
elif (( $age >= 41 && $age <= 60 )); then
echo "中年"
else
echo "老年"
fi
运行结果1:
19
成年运行结果2:
100
老年
再举一个例子,输入一个整数,输出该整数对应的星期几的英文表示:
#!/bin/bash
printf "Input integer number: "
read num
if ((num==1)); then
echo "Monday"
elif ((num==2)); then
echo "Tuesday"
elif ((num==3)); then
echo "Wednesday"
elif ((num==4)); then
echo "Thursday"
elif ((num==5)); then
echo "Friday"
elif ((num==6)); then
echo "Saturday"
elif ((num==7)); then
echo "Sunday"
else
echo "error"
fi
运行结果1:
Input integer number: 4
Thursday运行结果2:
Input integer number: 9
error
三十、Shell退出状态
每一条 Shell 命令,不管是 Bash 内置命令(例如 cd、echo),还是外部的 Linux 命令(例如 ls、awk),还是自定义的 Shell 函数,当它退出(运行结束)时,都会返回一个比较小的整数值给调用(使用)它的程序,这就是命令的退出状态(exit statu)。
很多 Linux 命令其实就是一个C语言程序,熟悉C语言的读者都知道,main() 函数的最后都有一个return 0
,如果程序想在中间退出,还可以使用exit 0
,这其实就是C语言程序的退出状态。当有其它程序调用这个程序时,就可以捕获这个退出状态。
if 语句的判断条件,从本质上讲,判断的就是命令的退出状态。
按照惯例来说,退出状态为 0 表示“成功”;也就是说,程序执行完成并且没有遇到任何问题。除 0 以外的其它任何退出状态都为“失败”。
之所以说这是“惯例”而非“规定”,是因为也会有例外,比如 diff 命令用来比较两个文件的不同,对于“没有差别”的文件返回 0,对于“找到差别”的文件返回 1,对无效文件名返回 2。
有编程经验的读者请注意,Shell 的这个部分与你所熟悉的其它编程语言正好相反:在C语言、C++、Java、Python中,0 表示“假”,其它值表示“真”。
在 Shell 中,有多种方式取得命令的退出状态,其中《七、$?》是最常见的一种。上节《二十九、Shell if else语句》中使用了(())进行数学计算,我们不妨来看一下它的退出状态。请看下面的代码:
#!/bin/bash
read a
read b
(( $a == $b ));
echo "退出状态:"$?
运行结果1:
26
26
退出状态:0
运行结果2:
17
39
退出状态:1
退出状态和逻辑运算符的组合
Shell if 语句的一个神奇之处是允许我们使用逻辑运算符将多个退出状态组合起来,这样就可以一次判断多个条件了。
运算符 | 使用格式 | 说明 |
---|---|---|
&& | expression1 && expression2 | 逻辑与运算符,当 expression1 和 expression2 同时成立时,整个表达式才成立。 如果检测到 expression1 的退出状态为 0,就不会再检测 expression2 了,因为不管 expression2 的退出状态是什么,整个表达式必然都是不成立的,检测了也是多此一举。 |
|| | expression1 || expression2 | 逻辑或运算符,expression1 和 expression2 两个表达式中只要有一个成立,整个表达式就成立。 如果检测到 expression1 的退出状态为 1,就不会再检测 expression2 了,因为不管 expression2 的退出状态是什么,整个表达式必然都是成立的,检测了也是多此一举。 |
! | !expression | 逻辑非运算符,相当于“取反”的效果。如果 expression 成立,那么整个表达式就不成立;如果 expression 不成立,那么整个表达式就成立。 |
【实例】将用户输入的 URL 写入到文件中。
#!/bin/bash
read filename
read url
if test -w $filename && test -n $url
then
echo $url > $filename
echo "写入成功"
else
echo "写入失败"
fi
在 Shell 脚本文件所在的目录新建一个文本文件并命名为 urls.txt,然后运行 Shell 脚本,运行结果为:
urls.txt↙
http://c.biancheng.net/shell/↙
写入成功
test 是 Shell 内置命令,可以对文件或者字符串进行检测,其中,-w
选项用来检测文件是否存在并且可写,-n
选项用来检测字符串是否非空。>
表示重定向,默认情况下,echo 向控制台输出,这里我们将输出结果重定向到文件。