与 cat 相比,Bash 的读取循环非常慢,为什么?
- 2024-10-09 09:11:00
- admin 原创
- 83
问题描述:
这里有一个简单的测试脚本:
while read LINE; do
LINECOUNT=$(($LINECOUNT+1))
if [[ $(($LINECOUNT % 1000)) -eq 0 ]]; then echo $LINECOUNT; fi
done
当我这样做时,cat my450klinefile.txt | myscript
CPU 锁定在 100%,它每秒可以处理大约 1000 行。处理cat my450klinefile.txt >/dev/null
半秒内完成的工作大约需要 5 分钟。
有没有更有效的方法来实现这一点?我只需要从 stdin 读取一行,计算字节数,然后将其写入命名管道。但即使是这个例子的速度也慢得令人难以置信。
每 1Gb 的输入行我都需要执行一些更复杂的脚本操作(关闭和打开一些输入数据的管道)。
解决方案 1:
速度如此缓慢的原因while read
是 shell 需要对每个字节进行系统调用。它无法从管道读取大缓冲区,因为 shell 不能从输入流读取多行,因此必须将每个字符与换行符进行比较。如果您strace
在while read
循环中运行,就会看到这种行为。这种行为是可取的,因为它可以可靠地执行以下操作:
while read size; do test "$size" -gt 0 || break; dd bs="$size" count=1 of=file$(( i++ )); done
其中循环内的命令与 shell 读取的是同一个流。如果 shell 通过读取大型缓冲区消耗了大量数据,内部命令将无法访问这些数据。不幸的是,这样做的副作用是read
速度非常慢。
解决方案 2:
这是因为bash
在这种情况下,脚本是解释性的,并没有真正针对速度进行优化。通常,最好使用以下外部工具之一:
awk 'NR%1000==0{print}' inputFile
这与“每 1000 行打印一次”示例相匹配。
如果您希望(对于每一行)输出字符数,然后输出行本身,并通过另一个进程将其传输,那么您也可以这样做:
awk '{print length($0)" "$0}' inputFile | someOtherProcess
与解释型 shell 脚本相比,awk
诸如sed
、、、等更强大的工具更适合这些任务。grep
`cut`perl
解决方案 3:
计算每个字符串的字节数的 perl 解决方案:
perl -p -e '
use Encode;
print length(Encode::encode_utf8($_))."
";$_=""'
例如:
dd if=/dev/urandom bs=1M count=100 |
perl -p -e 'use Encode;print length(Encode::encode_utf8($_))."
";$_=""' |
tail
对我来说是 7.7Mb/s
比较一下使用了多少脚本:
dd if=/dev/urandom bs=1M count=100 >/dev/null
运行速度为 9.1Mb/s
看来脚本不是那么慢:)
解决方案 4:
不太确定你的脚本应该做什么。所以这可能不是对你问题的回答,而是一个通用提示。
不要cat
将您的文件通过管道传输到您的脚本,而是在使用 bash 脚本从文件读取时执行以下操作:
while read line
do
echo $line
done <file.txt
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件