POSIX 指定的命令包括expr命令和bc命令。expr
功能并不完全相同,例如可以使用正则表达式进行比较,而bc
命令可以进行高级算术计算,但它们具有作为常用功能的数值计算功能。然而,即使使用相同的计算,这两个命令在设计上也有很大的不同。在本文中,我将解释如何设计一个适合 shell 脚本的命令,因为这两个命令之间存在差异。智程网络科技_智能营销笔记本软件开发_大数据营销笔记本系统定制_营销软件-曲阜市智程网络科技有限公司
expr
bc
只需进行简单的计算,您就会注意到差异。例如1 + 2
,要做:
$ expr 1 + 2 3 $ echo "1 + 2" | bc 3
命令多么bc
麻烦!
无论如何,它expr
更直观。管道计算公式是多余的。echo
你需要什么?在我认真学习shell脚本之前,我是这么认为的。
注意$((1 + 2))
可以在POSIX shell 中使用算术展开计算相同的值(示例)。这是expr
推荐的使用方法。但现在,忘记它。
许多bc
命令解释的不幸部分是它们仅以以下方式解释。
# 計算を行います echo "1 + 2" | bc # 計算を行い変数に入れます ret=$(echo "1 + 2" | bc)
重要的用法没有解释。这就是它的使用方式。
$ cat data.txt 1 + 2 2 + 3 3 + 4 $ cat data.txt | bc # または bc < data.txt 3 5 7
换句话说bc
,命令是一个过滤器,它可以通过传递一个公式列表来执行一系列计算。
bc
该命令在许多情况下awk
可以替换。此外,尽管 POSIX 指定了命令,但默认情况下不会安装它,具体取决于分发和配置。因此,貌似使用的机会不多,也没有给出详细的解释,但我现在认为,bc
命令最初是通过传递一个公式列表来计算的。
例如,如果您有以下 CSV 格式的文件,并且想要获取每个项目的总和
$ cat data.txt 1,2,3 10,20,30 100,200,300
这样,您bc
只需一个命令即可进行计算。
$ cat data.txt | sed 's/,/+/g' | bc 6 60 600
如果要进行复杂的计算,可以通过在文件中定义函数来完成。
$ cat func.bc # vim によると bc コマンド用のプログラムファイルの拡張子は bc らしい define f(v) { # POSIX 規定では関数名・変数名は 1 文字だけ・・・ return (v * v + 100) } $ cat data.txt f(1) f(2) f(3)
$ cat data.txt | bc func.bc 101 104 109
从 shell 脚本计算时的设计expr
差异bc
也会影响 shell 脚本的性能。
首先,请注意外部命令通常启动缓慢。(是外部命令的“启动”,而不是外部命令本身的慢。)换句话说,重要的是减少外部命令的调用次数,以免恶化shell的性能脚本。
表达式.sh
i=0 while [ "$i" -lt 10000 ]; do expr "$i" + 10000 i=$((i+1)) done
bc.sh
i=0 while [ "$i" -lt 10000 ]; do echo "$i" + 10000 i=$((i+1)) done | bc
$ time ./expr.sh > /dev/null real 0m5.716s user 0m4.913s sys 0m1.343s $ time ./bc.sh > /dev/null real 0m0.063s user 0m0.031s sys 0m0.091s
如您所见,bc
使用命令的速度要快得多。expr
由于该命令旨在将计算公式作为参数传递,因此每次计算都需要调用外部命令。另一方面bc
,该命令只能调用一次,因为公式可以作为列表传递。
一旦你理解了这一点,你会发现你最初认为只是烦人bc
的命令实际上更好。
后记
以下代码是循环调用命令并计算的结果bc
。expr
上面的代码显示bc
命令更快,但基于适用于结论bc
“命令更快”和“管道更快”的具体情况 它比调用命令更慢,因为它会生成子shell 和管道。有关详细信息,请参阅“ Shell 脚本使用管道并行处理并且速度很快......是一个神话!? ”。expr
bc
bc2.sh
i=0 while [ "$i" -lt 10000 ]; do echo "$i + 10000" | bc i=$((i+1)) done
$ time ./bc2.sh > /dev/null real 0m7.945s user 0m8.018s sys 0m2.646s
让我们在这里谈谈shell(脚本)。expr
命令和bc
命令并不完全是 shell 脚本的一部分。它是一个外部命令,只要不小心从 shell 脚本调用,就可以从其他语言调用。需要正确识别您正在使用的函数是shell脚本函数还是从shell脚本调用的外部函数(外部命令)。
很多“shell脚本”的描述并没有明确区分它们,而且我经常以为我在说shell脚本的语法,但不知不觉中,我在说如何使用外部命令...... (如果您将其视为使用 shell 脚本对“工作”的解释,我认为这无济于事。)
在引入 POSIX shell(如 bash)之前,命令通常用于 Bourne shell 中的数值计算expr
。(哦,准确的说好像用过。因为时代不同……)当然bc
可以用命令,但是循环变量加1等处理bc
不能传递计算公式列表。所以这没有意义。即使创建列表并将其通过管道传递给命令是一种很好的模式,但创建列表并不总是一种方便的方式。
expr
如前所述,命令启动(即外部命令启动)很慢。在从 Bourne shell 演变为 POSIX shell 的过程中添加的功能似乎增加了 shell 本身执行需要如此缓慢的外部命令调用的事情的能力。sed
添加了其他tr
参数扩展,可以减少对命令的依赖。
expr
算术扩展已经出现在POSIX shell中,作为命令计算的替代方法。expr
该命令还有其他正则表达式字符串匹配的特性,所以它不是算术扩展的完全替代品,但你不必expr
使用慢,至少对于整数的四种算术运算。除非您需要支持 POSIX shell 和更早版本,否则对算术运算使用算术扩展。
expr
使用算术扩展而不是命令(用于破折号)时的执行速度。它的速度非常快,因为它由 shell 本身处理,无需使用外部命令。
算法.sh
i=0 while [ "$i" -lt 10000 ]; do echo "$((i + 10000))" i=$((i+1)) done
$ time ./arith.sh > /dev/null real 0m0.031s user 0m0.030s sys 0m0.001s
作为奖励,它还显示了使用时的执行速度seq
(不是 POSIX 指定的命令) 。awk
在这个例子中,生成的数据量很大,所以即使外部命令启动缓慢,使用外部命令也比使用慢的shell脚本要快。哪个更快取决于处理内容和数据量。
seq.sh
seq -f '%.0f + 10000' 0 9999 | bc
awk.sh
awk 'BEGIN { for(i=0; i<10000; i++) print i "+10000" }' | bc # 余談 この awk コードってどう見ても手続き型プログラミングですよね?
$ time ./seq.sh > /dev/null real 0m0.016s user 0m0.020s sys 0m0.002s
$ time ./awk.sh > /dev/null real 0m0.016s user 0m0.020s sys 0m0.001s
回归故事。这篇文章是“一个很好的设计”shell脚本“。重要的一点不是shell 脚本的设计,而是“为”shell 脚本的设计。
shell脚本端也bc
得写代码,通过管道将数据传递给命令,但在此之前,expr
用命令传递数据的命令,像命令一样带参数传递数据的shell脚本是不可能的...... 作为先决条件,bc
您必须创建一个具有类似命令设计的程序。
expr
命令是用什么语言bc
写的?是的,它是C语言。(尽管可能还有其他实现。)这个故事似乎是关于 shell 脚本的,而不仅仅是 shell 脚本。这都是关于 CLI 命令的,因此它适用于您以 Ruby、Python、Go、Rust 或 CLI 命令实现的任何东西,而不仅仅是 C。
“9. 让每个程序都成为过滤器”是UNIX 哲学中的一句话,但无论您使用哪种语言,所有程序(几乎几乎是 CLI)。我认为这是一个有限的故事。不会有 GUI time) 是充当过滤器的词,也就是说,充当命令expr
而不是命令。bc
(在我的解释中,这个词并不意味着“所有程