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` 命令在脚本中似乎特别容易被过度使用。
 | 某些运算符,特别是 `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 |
|
 | 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`。
以模块化和连贯的形式编写脚本, 以便在必要时可以重新组织和精简它们。 一些适用于高级语言的优化技术可能适用于脚本,但其他技术(例如循环展开)大多是不相关的。 最重要的是,运用常识。
要获得关于优化如何显着减少脚本执行时间的出色演示,请参阅 示例 16-47。