在日常运维或开发中,经常需要执行大量Shell命令处理日志、文件或网络数据。比如你写了个脚本清理服务器旧日志,结果跑了一分钟才完成,而别人类似的脚本几秒就搞定——差别往往出在细节上。
减少管道和子进程的滥用
很多人习惯用多个管道串联命令,像这样:
cat access.log | grep "404" | awk '{print $1}' | sort | uniq -c
其实cat在这里是多余的,grep可以直接读文件。更高效写法:
grep "404" access.log | awk '{print $1}' | sort | uniq -c
每少一个管道,就少一次进程创建开销。当处理大文件时,这种优化效果明显。
优先使用内置命令而非外部工具
Bash有很多功能已经内置,不需要调用sed、awk也能完成简单操作。比如提取变量中的路径名:
filename="/var/log/system.log"
# 不推荐:调用外部命令
dirname $filename
# 推荐:使用bash内置语法
echo ${filename%/*}
内置操作几乎不耗时间,特别适合循环中频繁使用的场景。
避免在循环中频繁调用外部命令
下面这段代码很常见,但效率极低:
for file in *.txt; do
lines=$(wc -l < "$file")
echo "$file: $lines lines"
done
每次循环都启动一次wc进程。更好的方式是一次性处理:
wc -l *.txt | while read count file; do
if [[ "$file" != "total" ]]; then
echo "$file: $count lines"
fi
done
这样只启动一次wc,性能提升显著。
合理利用并行处理
如果你有多个独立任务,比如压缩不同目录的日志,完全可以并行执行:
tar -czf app.log.tar.gz app.log &
tar -czf nginx.log.tar.gz nginx.log &
tar -czf mysql.log.tar.gz mysql.log &
wait
加上&放到后台运行,最后用wait等待全部完成。在多核机器上,速度可能提升好几倍。
选择更快的解释器
默认用/bin/sh或/bin/bash,但在某些系统上dash更快。比如Debian系中,dash是默认的/bin/sh,启动速度比bash快很多。
如果你的脚本只用POSIX标准功能,可以改成:
#!/bin/sh
# 而不是 #!/bin/bash
尤其在开机脚本或高频调用场景下,这点差异会累积放大。
缓存重复计算的结果
有些信息不需要每次都查,比如获取本机IP:
get_local_ip() {
if [ -f /tmp/local_ip ] && [ $(find /tmp/local_ip -mmin -60) ]; then
cat /tmp/local_ip
else
hostname -I | awk '{print $1}' > /tmp/local_ip
cat /tmp/local_ip
fi
}
一小时内重复调用直接读缓存,避免反复解析。
这些方法看起来小,但在自动化脚本、监控任务或CI/CD流程中叠加起来,能省下不少等待时间。关键是养成“少开进程、少调外部、多用内置”的习惯,让Shell脚本真正跑得快起来。