因此,一般来说,我倾向于寻求sed
文本处理 - 特别是对于大文件 - 并且通常避免在 shell 本身中执行此类操作。
不过,我认为这可能会改变。我四处闲逛man ksh
,注意到了这一点:
<#pattern Seeks forward to the beginning of the
next line containing pattern.
<##pattern The same as <# except that the por‐
tion of the file that is skipped is
copied to standard output.
由于对现实世界的实用性持怀疑态度,我决定尝试一下。我做了:
seq -s'foo bar
' 1000000 >file
...对于一百万行数据,如下所示:
1foo bar
...
999999foo bar
1000000
...并将其与sed
以下内容进行比较:
p='^[^0-8]99999.*bar'
for c in "sed '/$p/q'" "ksh -c ':<##@(~(E)$p)'"
do </tmp/file eval "time ( $c )"
done | wc -l
所以这两个命令都应该达到999999foo酒吧为此,它们的模式匹配实现必须至少评估每行的开头和结尾。他们还必须根据否定模式验证第一个字符。这是一件简单的事情,但是......结果并不是我所期望的:
( sed '/^[^0-8]99999.*bar/q' ) \
0.40s user 0.01s system 99% cpu 0.419 total
( ksh -c ':<##@(~(E)^[^0-8]99999.*bar)' ) \
0.02s user 0.01s system 91% cpu 0.033 total
1999997
ksh
这里使用 ERE 和sed
BRE。我之前用 shell 模式做了同样的事情,ksh
但结果没有什么不同。
不管怎样,这是一个相当显着的差异——ksh
超过sed
了 10 倍。我之前读过 David Korn 编写了他自己的 io lib 并在其中实现了它ksh
- 这可能是相关的? - 但我对此几乎一无所知。 shell 为何能做到如此出色?
对我来说更令人惊奇的是,它ksh
确实在你要求的地方留下了它的偏移量。要得到(几乎)同样出自(GNU) sed
你必须使用-u
-非常慢。
这是一个grep
v.ksh
测试:
1000000 #grep + head
( grep -qm1 '^[^0-8]99999.*bar'; head -n1; ) \
0.02s user 0.00s system 90% cpu 0.026 total
999999foo bar #ksh + head
( ksh -c ':<#@(~(E)^[^0-8]99999.*bar)'; head -n1; ) \
0.02s user 0.00s system 73% cpu 0.023 total
ksh
这里节拍grep
- 但并不总是 - 他们几乎是紧密相连的。不过还是很优秀了和 ksh
提供前瞻 -head
的输入开始前它的匹配。
我想,这似乎好得令人难以置信。这些命令在幕后有什么不同的作用?
哦,显然这里甚至没有子外壳:
ksh -c 'printf %.5s "${<file;}"'
答案1
ksh 不仅使用sfio但它使用自己的自定义内存分配器。
尽管如此,我的猜测是 sfio 在这种情况下有所作为。我只是尝试在 strace 下运行您的示例,可以看到 ksh 调用读/写约 200 次(65 KB 块),而 sed 调用读/写约 3400 次(4 KB 块)。使用 sed -u 我的笔记本电脑几乎融化了,读取是按字节完成的,写入是按行完成的。 Ksh 简单地使用 lseek。 Grep 使用读取约 400 次(32 KB 块)。