编程语言的一项至关重要的特性是数学运算能力。在shell脚本中,执行数学运算有两种方式。
expr命令
expr 命令能够识别少量算术运算符和字符串运算符。
$ expr 1 + 5
6
尽管标准运算符在expr命令中工作得很好,但在脚本或命令行中使用时仍有问题出现。许多expr命令运算符在shell中另有他意(比如*)。当这些符号出现在expr命令中时,会产生一些诡异的结果:
$ expr 5 * 2
expr: syntax error
要解决这个问题,就要在那些容易被shell错误解释的字符被传入expr命令之前,使用shell的转义字符(反斜线)对其进行转义:
$ expr 5 \* 2
10
真够难看的!在shell脚本中使用expr命令也同样麻烦:
$ cat test
#!/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 test
$ ./test
The result is 2
幸好,bash shell针对数学运算作出了改进,接下来将介绍。
使用方括号
在bash中,要将数学运算结果赋给变量,可以使用$ 和方括号($[ operation]):
$ var1=$[1 + 5]
$ echo $var1
6
$ var2=$[$var1 * 2]
$ echo $var2
12
用方括号来执行数学运算要比expr命令方便得多。这种技术也适用于shell脚本:
$ cat test1
#!/bin/bash
var1=100
var2=50
var3=45
var4=$[$var1 * ($var2 - $var3)]
echo The final result is $var4
运行这个脚本会得到如下输出:
$ chmod u+x test1
$ ./test1
The final result is 500
在使用方括号执行数学运算时,无须担心shell会误解乘号或其他符号。shell清楚方括号内的星号不是通配符。
在bash shell脚本中执行数学运算有一个很大的局限。来看下面这个例子:
$ cat test2
#!/bin/bash
var1=100
var2=45
var3=$[$var1 /$var2]
echo The final result is $var3
运行该脚本,看看会发生什么:
$ chmod u+x test2
$ ./test2
The final result is 2
bash shell的数学运算符只支持整数运算。如果打算尝试现实世界中的数学运算,那么这是一个巨大的限制。
浮点数解决方案
最常见的做法是使用内建的bash计算器bc。
bc的基本用法
bash计算器实际上是一种编程语言,允许在命令行中输入浮点数表达式,然后解释并计算该表达式,最后返回结果。bash计算器能够识别以下内容。
- 数字(整数和浮点数)
- 变量(简单变量和数组)
- 注释(以#或C语言中的/* */开始的行)
- 表达式
- 编程语句(比如if-then语句)
- 函数
你可以在shell提示符下通过bc命令访问bash计算器(-q选项可以不显示bash计算器冗长的欢迎信息):
$ bc -q
12 * 5.4
64.8
3.156 * (3 + 5)
25.248
quit
除了普通数字,bash计算器还支持变量:
$ bc -q
var1=10
var1 * 4
40
var2 = var1 / 5
print var2
2
quit
变量值一旦被定义,就可以在整个bash计算器会话中使用了。print语句可以打印变量和数字。
在脚本中使用bc
现在你可能想问bash计算器是如何在shell脚本中处理浮点数运算的。还记得反引号吗?没错,可以用命令替换来运行bc命令,将输出赋给变量。基本格式如下:
variable=$(echo "options; expression" | bc)
第一部分的options允许你设置变量。如果需要多个变量,可以用分号来分隔它们。expression定义了要通过bc执行的数学表达式。下面是在脚本中执行此操作的示例:
$ cat test3
#!/bin/bash
var1=$(echo " scale=4; 3.44 / 5" | bc)
echo The answer is $var1
这个例子将scale变量设置为4位小数,在expression部分指定了特定的运算。运行这个脚本会产生如下输出:
$ chmod u+x test3
$ ./test3
The answer is .6880
太好了!表达式中不仅可以使用数字,还可以用shell脚本中定义好的变量:
$ cat test4
#!/bin/bash
var1=100
var2=45
var3=$(echo "scale=4; $var1 / $var2" | bc)
echo The answer for this is $var3
该脚本定义了两个变量,这两个变量都可以用在expression部分中,发送给bc命令。别忘了$表示引用变量的值而不是变量自身。该脚本的输出如下:
$ ./test4
The answer for this is 2.2222
这种方法适用于较短的运算,但有时你需要涉及更多的数字。如果要进行大量运算,那么在一个命令行中列出多个表达式容易让人犯晕。
有一种方法可以解决这个问题。bc命令能接受输入重定向,允许将一个文件重定向到bc命令来处理。但这同样会让人头疼,因为还需要将表达式存放到文件中。
最好的办法是使用内联输入重定向,它允许直接在命令行中重定向数据。在shell脚本中,可以将输出赋给一个变量:
variable=$(bc << EOF
options
statements
expressions
EOF
)
字符串EOF标识了内联重定向数据的起止。别忘了,仍然需要用命令替换符将bc命令的输出赋给变量;
现在,可以将bash计算器涉及的各个部分放入脚本文件的不同行中。以下示例演示了如何在脚本中使用这项技术:
$ cat test5
#!/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计算器中创建的变量仅在计算器中有效,不能在shell脚本中使用。