如果您正在阅读这篇文章,很可能您不仅熟悉计算机系统上的命令行,而且非常习惯使用命令行来排除图形界面。我理解——自从命令行是计算领域中唯一的选择以来,我就一直在使用它,甚至在过去还为 BSD Unix 贡献了代码。现代 GUI 有很多优点,使其更胜一筹,但就功能、速度和灵活性而言,命令行仍然很强大,如果您可以快速打字,则更是如此。
然而,典型的 Unix 或 Linux 系统中的命令行缺少一些功能,尤其是一个强大而灵活的计算器。您可以使用 expr
甚至 shell $(( ))
符号,但它们非常有限,当您尝试解决 10/3 或任何非简单整数数学的问题时,这一点会立即显现出来。
您可以使用基本计算器 bc,但这是一个只有饱经风霜的老派黑客才可能喜欢的语言工具,并且很难理解它多年来在系统中的用途。bc 拥有笨拙的界面和明显缺乏有用的命令行选项,它仍然是操作系统的一部分,这真是一个谜。
幸运的是,我们谈论的是 Linux,这意味着我们可以通过围绕提供我们所需原始功能的工具封装一个更好的界面来解决问题——在 Linux 术语中,这是一个“包装程序”,原因显而易见。
这就是 Bash shell 脚本 calc 所做的事情——提供一个简单、用户友好的命令行计算器。它甚至具有有用的默认设置,因此您不必记住在解决 10/3 之前将十进制精度设置为非零。
使用 bc
bc 被称为任意精度二进制计算器,但奇怪的是,它的默认行为是在交互式 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
语句、停止、中断、循环管理的 continue 和函数。但让我们坦诚地说:如果您真的想编写一个简洁的程序来解决数学方程式,那么有更好的选择,从 Perl 到大型、超强大的工具,如 Matlab。
尽管我是创造性功能蔓延精神的忠实拥护者,但也应该认识到给定程序的局限性和功能,而不是花费数天时间使其更复杂,而是接受它可以解决某些问题——但并非所有问题。实际上,尽管 bc 支持函数、命令流和变量,但我怀疑您很难在现代 Unix 或 Linux 系统上找到一个利用此功能的单个脚本,并且可以从 bc 中剥离出来而无人察觉。
对于简单问题的快速、简单的解决方案一直是命令行界面的巨大优势,也是使 Unix 系统设计如此强大的原因。《Wicked Cool Shell Scripts 2nd Edition》这本书中,我的合著者和我非常详细地探讨了这个概念,提供了 100 多个 Bash shell 脚本来让人惊叹和高兴。但更根本的是,无论您是在 Mac OS 命令行、Linux 系统还是老式的 Unix 服务器上,我们都在使用脚本在命令行这面砖墙上添加一层新的砂浆。
如果您偶尔使用命令行,您会惊讶于我们的脚本集合(例如 calc)对您有多么大的帮助。毕竟,宏伟的建筑是由小砖块和小铲子的砂浆建造的,对吗?
14 条评论