如果您正在阅读本文,很可能您不仅熟悉计算机系统上的命令行,而且非常习惯于使用命令行来排除图形界面。我理解——自从命令行是计算领域唯一的选择以来,我就一直在使用命令行,甚至在过去还为 BSD Unix 贡献了代码。现代 GUI 有很多优点,使其优于命令行,但在功能、速度和灵活性方面,命令行仍然很强大,如果您能快速打字,那就更强大了。
然而,典型的 Unix 或 Linux 系统的命令行中缺少一些功能,特别是强大而灵活的计算器。您可以使用 expr
甚至 shell $(( ))
符号,但它们非常有限,当您尝试求解 10/3 或任何不是简单整数数学的东西时,这一点会立即显现出来。
您可以使用基本计算器 bc,但它是一个工具,其语言只有久经沙场的老派黑客才可能喜欢,并且很难理解它多年来在系统中的用途。bc 配备了笨拙的界面和明显缺乏有用的命令行选项,它仍然是操作系统的一部分,这真是个谜。
幸运的是,我们正在讨论 Linux,这意味着我们可以通过围绕提供我们所需原始功能的工具包装更好的界面来解决问题——在 Linux 术语中,这是一个“包装程序”,原因显而易见。
这就是 Bash shell 脚本 calc 所做的——提供一个简单、用户友好的命令行计算器。它甚至具有有用的默认值,因此您不必记住在求解 10/3 之前将十进制精度设置为非零。
使用 bc
bc 被标榜为任意精度二进制计算器,但奇怪的是,它的默认行为是在交互式 shell 中仅使用整数值,而该 shell 没有任何提示。这是一个典型的交互
$ bc
1+1
2
10/3
3
quit
输入一个数学方程式,它会求解并显示结果。但是 10/3 仍然是 3?为了解决这个问题,bc 用户很快熟悉了 scale
,它允许您指定显示结果的精度;更高的 scale 提供小数点后更多的位数。就像在
10/3
3
scale=4
10/3
3.3333
scale=20
10/3
3.33333333333333333333
quit
这些信息足以说明如何生成一个简单的命令行友好的浮点计算器作为 shell 脚本
#!/bin/sh
cat << EOF | bc
scale=2
$*
EOF
exit 0
这个想法很简单:无论用户将什么指定为命令的参数,都直接将其馈送到 bc,但首先将 scale 设置为 2。在实践中,它已经很有用了
$ calc '(100/3) * 2 + (11 + 333.5)'
411.16
还不错。但是,让我们将这个简单的想法变成一个交互式计算器 shell,您可以在其中打开一个窗口,并且任何时候遇到方程式,只需复制/粘贴它并快速求解。
Calc:交互式计算器
上面的小 shell 脚本可以轻松地转换为一个简单的函数,最终看起来像这样
scriptbc()
{
scale=$1 ; shift
cat << EOF | $bc
scale=$scale
$*
EOF
}
在 shell 脚本中调用此函数时,您需要记住的是,第一个参数始终是所需的 scale,否则 bc 肯定会有点困惑。
但那是艰苦的工作。现在主循环出奇地简洁
while read command args
do
case $command
in
quit|exit) exit 0 ;;
help|\?) show_help ;;
scale) scale=$args ;;
*) scriptbc $scale "$command" "$args" ;;
esac
/bin/echo -n "calc> "
done
不太复杂,而且还添加了一个帮助函数。请注意 Bash shell 让您在 while 语句中与 read
命令一起使用的巧妙方式——read
总是将用户键入的内容分解为每个列出的变量一个单词,其余所有内容都放在给定的最后一个变量中。因此,如果用户键入 1+1,则 read command args
表示 command="1+1
",但如果用户键入 "1 + 1",则 command="1
" 且 args="+ 1
"。在这两种情况下,它都能正常工作,但是,当然,这是为了让用户也可以指定命令词。
一些额外的 echo 语句使事情变得漂亮,我们得到了一个真正可用的交互式计算器,具有许多功能,所有功能都由 bc 提供支持
$ calc
Calc--a simple calculator. Enter 'help' for help, 'quit' to quit.
calc> help
In addition to standard math functions, calc also supports:
a%b = remainder of a/b
a^b = exponential: a raised to the b power
s(x) = sine of x, x in radians
c(x) = cosine of x, x in radians
a(x) = arctangent of x, in radians
l(x) = natural log of x
e(x) = exponential log of raising e to the x
j(n,x) = Bessel function of integer order n of x
scale N = show N fractional digits (default = 2)
calc> s(1)
.84
calc> 100 + (10 * 3.55)
135.50
calc> 5545 + 11 – 4.55
5551.45
calc> 10 / 3
3.33
calc> quit
尽管它的界面可能很奇怪,但事实证明 bc 还有其他绝招,包括用户设置和使用变量的能力,使其更像是一种数学编程语言。问题是,此脚本每次调用 bc 一次,因此无法在调用之间保留状态。这意味着,尽管用户可以输入诸如 cars=25
之类的语句,但如果他们在下一行引用该变量,它将从 bc 的内存中消失。
bc 具有隐藏的超能力,但是……
bc 还支持各种编程结构,包括 if
、while
和 for
语句、halts、breaks、continues 用于循环管理以及函数。但让我们坦诚地说:如果您真的想编写一个简洁的程序来求解数学方程式,那么有更好的选择,从 Perl 到大型、超级强大的工具,例如 Matlab。
尽管我非常喜欢渐进式功能主义背后的创造精神,但认识到给定程序的局限性和功能也很重要,与其花费数天使其更复杂,不如接受它可以解决某些问题——但并非所有问题。事实上,尽管 bc 支持函数、命令流和变量,但我怀疑您很难在现代 Unix 或 Linux 系统上找到一个利用此功能的脚本,并且可以将其从 bc 中剥离出来而不会有人注意到。
对于简单的问题,快速、简单的解决方案一直是命令行界面的巨大优势,也是使 Unix 系统设计如此强大的原因。在 《Wicked Cool Shell Scripts 2nd Edition》 一书中,我的合著者和我非常详细地探讨了这个概念,提供了 100 多个 Bash shell 脚本来令人惊叹和愉悦。但从根本上说,我们正在使用脚本在命令行这堵砖墙上添加一层新的砂浆,无论您是在 Mac OS 命令行、Linux 系统还是老式 Unix 服务器上。
如果您偶尔使用命令行,您会惊讶于我们的脚本集合(例如 calc)对您有多大帮助。毕竟,伟大的建筑是由小砖块和小铲砂浆建造的,对吧?
14 条评论