智能营销笔记本服务商

营销笔记本+万能采集+AI名片+智能电销+短信群发=同步管理

免费咨询热线: 15064770313

shell脚本的好设计,与expr和bc的设计区别

介绍

POSIX 指定的命令包括expr命令和bc命令。expr功能并不完全相同,例如可以使用正则表达式进行比较,而bc命令可以进行高级算术计算,但它们具有作为常用功能的数值计算功能。然而,即使使用相同的计算,这两个命令在设计上也有很大的不同。在本文中,我将解释如何设计一个适合 shell 脚本的命令,因为这两个命令之间存在差异。智程网络科技_智能营销笔记本软件开发_大数据营销笔记本系统定制_营销软件-曲阜市智程网络科技有限公司

参数与管道

exprbc只需进行简单的计算,您就会注意到差异。例如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 命令

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

expr 和 bc 之间的设计差异对 shell 脚本性能的影响

从 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的命令实际上更好。

后记

以下代码是循环调用命令并计算的结果bcexpr上面的代码显示bc命令更快,但基于适用于结论bc“命令更快”和“管道更快”的具体情况 它比调用命令更慢,因为它会生成子shell 和管道。有关详细信息,请参阅“ Shell 脚本使用管道并行处理并且速度很快......是一个神话!? ”。exprbc

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

UNIX 哲学

回归故事。这篇文章是“一个很好的设计”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

(在我的解释中,这个词并不意味着“所有程