36.6. 优化

大多数 shell 脚本是针对非复杂问题的快速而粗略的解决方案。因此,优化它们以提高速度通常不是主要问题。 但是,考虑这样一种情况:一个脚本执行一项重要的任务,做得很好,但运行速度太慢。 用编译语言重写它可能不是一个可接受的选择。 最简单的解决方法是重写脚本中使其变慢的部分。 是否有可能将代码优化原则应用于一个简单的 shell 脚本?

检查脚本中的循环。重复操作消耗的时间会迅速累积。 如果可能,请从循环中移除耗时的操作。

优先使用内建命令,而不是系统命令。 内建命令执行速度更快,并且通常在调用时不会启动子 shell。

避免不必要的命令,尤其是在管道中。

cat "$file" | grep "$word"

grep "$word" "$file"

#  The above command-lines have an identical effect,
#+ but the second runs faster since it launches one fewer subprocess.
`cat` 命令在脚本中似乎特别容易被过度使用。

Note

某些运算符,特别是 `expr`,效率非常低下,可以用双括号算术扩展来代替。 请参阅 示例 A-59

Math tests

math via $(( ))
real          0m0.294s
user          0m0.288s
sys           0m0.008s

math via expr:
real          1m17.879s   # Much slower!
user          0m3.600s
sys           0m8.765s

math via let:
real          0m0.364s
user          0m0.372s
sys           0m0.000s

脚本中的条件测试结构值得仔细检查。 尽可能用 `case` 替换 `if-then` 结构,并在可能的情况下组合测试,以最大限度地减少脚本执行时间。 同样,请参考 示例 A-59

Test using "case" construct:
real          0m0.329s
user          0m0.320s
sys           0m0.000s


Test with if [], no quotes:
real          0m0.438s
user          0m0.432s
sys           0m0.008s


Test with if [], quotes:
real          0m0.476s
user          0m0.452s
sys           0m0.024s


Test with if [], using -eq:
real          0m0.457s
user          0m0.456s
sys           0m0.000s

Note

Erik Brandsberg 建议在大多数情况下优先使用关联数组,而不是传统的数字索引数组。 当覆盖数字数组中的值时,与关联数组相比,性能损失很大。 运行测试脚本证实了这一点。 请参阅 示例 A-60

Assignment tests

Assigning a simple variable
real          0m0.418s
user          0m0.416s
sys           0m0.004s

Assigning a numeric index array entry
real          0m0.582s
user          0m0.564s
sys           0m0.016s

Overwriting a numeric index array entry
real          0m21.931s
user          0m21.913s
sys           0m0.016s

Linear reading of numeric index array
real          0m0.422s
user          0m0.416s
sys           0m0.004s

Assigning an associative array entry
real          0m1.800s
user          0m1.796s
sys           0m0.004s

Overwriting an associative array entry
real          0m1.798s
user          0m1.784s
sys           0m0.012s

Linear reading an associative array entry
real          0m0.420s
user          0m0.420s
sys           0m0.000s

Assigning a random number to a simple variable
real          0m0.402s
user          0m0.388s
sys           0m0.016s

Assigning a sparse numeric index array entry randomly into 64k cells
real          0m12.678s
user          0m12.649s
sys           0m0.028s

Reading sparse numeric index array entry
real          0m0.087s
user          0m0.084s
sys           0m0.000s

Assigning a sparse associative array entry randomly into 64k cells
real          0m0.698s
user          0m0.696s
sys           0m0.004s

Reading sparse associative index array entry
real          0m0.083s
user          0m0.084s
sys           0m0.000s

使用 `time``times` 工具来分析计算密集型命令。 考虑用 C 甚至汇编语言重写对时间要求严格的代码段。

尽量减少文件 I/O。 Bash 在处理文件方面效率不高,因此请考虑在脚本中使用更合适的工具来处理文件,例如 `awk``Perl`

以模块化和连贯的形式编写脚本,[1] 以便在必要时可以重新组织和精简它们。 一些适用于高级语言的优化技术可能适用于脚本,但其他技术(例如循环展开)大多是不相关的。 最重要的是,运用常识。

要获得关于优化如何显着减少脚本执行时间的出色演示,请参阅 示例 16-47

注释

[1]

这通常意味着大量使用函数