另一个对任何编程语言都很重要的特性是操作数字的能力。遗憾的是,对shell脚本来说,这
个处理过程会比较麻烦。在shell脚本中有两种途径来进行数学运算。
11.7.1 expr 命令
最开始,Bourne shell提供了一个特别的命令用来处理数学表达式。expr命令允许在命令行
上处理数学表达式,但是特别笨拙。
$ expr 1 + 5
6
expr命令能够识别少数的数学和字符串操作符,见表11-1。
尽管标准操作符在expr命令中工作得很好,但在脚本或命令行上使用它们时仍有问题出现。
许多expr命令操作符在shell中另有含义(比如星号)。当它们出现在在expr命令中时,会得到一
些诡异的结果。
$ expr 5 2
expr: syntax error
$
要解决这个问题,对于那些容易被shell错误解释的字符,在它们传入expr命令之前,需要使
用shell的转义字符(反斜线)将其标出来。
$ expr 5 \ 2
10
$
现在,麻烦才刚刚开始!在shell脚本中使用expr命令也同样复杂:
$ cat test6
#!/bin/bash
# An example of using the expr command
var1=10
var2=20
var3=$(expr $var2 / $var1)
echo The result is $var3
要将一个数学算式的结果赋给一个变量,需要使用命令替换来获取expr命令的输出:
$ chmod u+x test6
$ ./test6
The result is 2
$
幸好bash shell有一个针对处理数学运算符的改进,你将会在下一节中看到。
11.7.2 使用方括号
bash shell为了保持跟Bourne shell的兼容而包含了expr命令,但它同样也提供了一种更简单
的方法来执行数学表达式。在bash中,在将一个数学运算结果赋给某个变量时,可以用美元符和
方括号($[ operation ])将数学表达式围起来。
$ var1=$[1 + 5]
$ echo $var1
6
$ var2=$[$var1 2]
$ echo $var2
12
$
用方括号执行shell数学运算比用expr命令方便很多。这种技术也适用于shell脚本。
$ cat test7
#!/bin/bash
var1=100
var2=50
var3=45
var4=$[$var1 ($var2 - $var3)]
echo The final result is $var4
$
运行这个脚本会得到如下输出。
$ chmod u+x test7
$ ./test7
The final result is 500
$
同样,注意在使用方括号来计算公式时,不用担心shell会误解乘号或其他符号。shell知道它
不是通配符,因为它在方括号内。
在bash shell脚本中进行算术运算会有一个主要的限制。请看下例:
$ cat test8
#!/bin/bash
var1=100
var2=45
var3=$[$var1 / $var2]
echo The final result is $var3
$
现在,运行一下,看看会发生什么:
$ chmod u+x test8
$ ./test8
The final result is 2
$
bash shell数学运算符只支持整数运算。若要进行任何实际的数学计算,这是一个巨大的限制。
说明 z shell(zsh)提供了完整的浮点数算术操作。如果需要在shell脚本中进行浮点数运算,可
以考虑看看z shell(将在第23章中讨论)。
11.7.3 浮点解决方案
有几种解决方案能够克服bash中数学运算的整数限制。最常见的方案是用内建的bash计算器,
叫作bc。
1. bc的基本用法
bash计算器实际上是一种编程语言,它允许在命令行中输入浮点表达式,然后解释并计算该
表达式,最后返回结果。bash计算器能够识别:
数字(整数和浮点数)
变量(简单变量和数组)
注释(以#或C语言中的/ /开始的行)
表达式
编程语句(例如if-then语句)
函数
可以在shell提示符下通过bc命令访问bash计算器:
$ bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type ‘warranty’.
12 5.4
64.8
3.156 (3 + 5)
25.248
quit
$
这个例子一开始输入了表达式12 5.4。bash计算器返回了计算结果。随后每个输入到计
算器的表达式都会被求值并显示出结果。要退出bash计算器,你必须输入quit。
浮点运算是由内建变量scale控制的。必须将这个值设置为你希望在计算结果中保留的小数
位数,否则无法得到期望的结果。
$ bc -q
3.44 / 5
0
scale=4
3.44 / 5
.6880
quit
$
scale变量的默认值是0。在scale值被设置前,bash计算器的计算结果不包含小数位。在将
其值设置成4后,bash计算器显示的结果包含四位小数。-q命令行选项可以不显示bash计算器冗
长的欢迎信息。
除了普通数字,bash计算器还能支持变量。
$ bc -q
var1=10
var1 4
40
var2 = var1 / 5
print var2
2
quit
$
变量一旦被定义,你就可以在整个bash计算器会话中使用该变量了。print语句允许你打印
变量和数字。
2. 在脚本中使用bc
现在你可能想问bash计算器是如何在shell脚本中帮助处理浮点运算的。还记得命令替换吗?
是的,可以用命令替换运行bc命令,并将输出赋给一个变量。基本格式如下:
variable=$(echo “options; expression” | bc)
第一部分options允许你设置变量。如果你需要不止一个变量,可以用分号将其分开。
expression参数定义了通过bc执行的数学表达式。这里有个在脚本中这么做的例子。
$ cat test9
#!/bin/bash
var1=$(echo “scale=4; 3.44 / 5” | bc)
echo The answer is $var1
$
这个例子将scale变量设置成了四位小数,并在expression部分指定了特定的运算。运行
这个脚本会产生如下输出。
$ chmod u+x test9
$ ./test9
The answer is .6880
$
太好了!现在你不会再只能用数字作为表达式值了。也可以用shell脚本中定义好的变量。
$ cat test10
#!/bin/bash
var1=100
var2=45
var3=$(echo “scale=4; $var1 / $var2” | bc)
echo The answer for this is $var3
$
脚本定义了两个变量,它们都可以用在expression部分,然后发送给bc命令。别忘了用美
元符表示的是变量的值而不是变量自身。这个脚本的输出如下。
$ ./test10
The answer for this is 2.2222
$
当然,一旦变量被赋值,那个变量也可以用于其他运算。
$ cat test11
#!/bin/bash
var1=20
var2=3.14159
var3=$(echo “scale=4; $var1 $var1” | bc)
var4=$(echo “scale=4; $var3 $var2” | bc)
echo The final result is $var4
$
这个方法适用于较短的运算,但有时你会涉及更多的数字。如果需要进行大量运算,在一个
命令行中列出多个表达式就会有点麻烦。
有一个方法可以解决这个问题。bc命令能识别输入重定向,允许你将一个文件重定向到bc
命令来处理。但这同样会叫人头疼,因为你还得将表达式存放到文件中。
最好的办法是使用内联输入重定向,它允许你直接在命令行中重定向数据。在shell脚本中,
你可以将输出赋给一个变量。
variable=$(bc << EOF
options
statements
expressions
EOF
)
EOF文本字符串标识了内联重定向数据的起止。记住,仍然需要命令替换符号将bc命令的输
出赋给变量。
现在可以将所有bash计算器涉及的部分都放到同一个脚本文件的不同行。下面是在脚本中使
用这种技术的例子。
$ cat test12
#!/bin/bash
var1=10.46
var2=43.67
var3=33.2
var4=71
var5=$(bc << EOF
scale = 4
a1 = ( $var1 $var2)
b1 = ($var3 $var4)
a1 + b1
EOF
)
echo The final answer for this mess is $var5
$
将选项和表达式放在脚本的不同行中可以让处理过程变得更清晰,提高易读性。EOF字符串
标识了重定向给bc命令的数据的起止。当然,必须用命令替换符号标识出用来给变量赋值的命令。
你还会注意到,在这个例子中,你可以在bash计算器中赋值给变量。这一点很重要:在bash
计算器中创建的变量只在bash计算器中有效,不能在shell脚本中使用。