ksh93怎么这么快?

mik*_*erv 9 shell ksh text-processing

所以,一般来说,我倾向于寻找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.
Run Code Online (Sandbox Code Playgroud)

对现实世界的实用性持怀疑态度,我决定尝试一下。我做了:

seq -s'foo bar
' 1000000 >file
Run Code Online (Sandbox Code Playgroud)

...对于一百万行数据,如下所示:

1foo bar
...
999999foo bar
1000000
Run Code Online (Sandbox Code Playgroud)

...并将其与以下内容相提并论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
Run Code Online (Sandbox Code Playgroud)

所以这两个命令都应该达到999999foo bar并且它们的模式匹配实现必须至少评估每行的开头和结尾才能这样做。他们还必须根据否定模式验证第一个字符。这是一件简单的事情,但是……结果并不是我所期望的:

( 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
Run Code Online (Sandbox Code Playgroud)

ksh这里使用 ERE 和sedBRE。我之前用ksh和 shell 模式做了同样的事情,但结果没有什么不同。

无论如何,这是一个相当大的差异 -ksh超过sed10 倍。我之前读过 David Korn 编写了他自己的 io lib 并在其中实现了它ksh- 这可能是相关的吗?- 但我对此几乎一无所知。shell 是如何做到这一点的?

更令我惊讶的是,它ksh确实在你要求的地方留下了它的偏移量。要从(GNU)中获得(几乎)相同的结果,您必须使用-非常慢 sed-u

这是一个grepv.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
Run Code Online (Sandbox Code Playgroud)

kshgrep在这里节拍- 但并不总是 - 他们几乎是捆绑在一起的。尽管如此,这仍然非常出色,并且 ksh提供了lookahead -head的输入匹配之前开始。

我想,这似乎太好了,难以置信。这些命令在幕后有何不同?

哦,显然这里甚至没有子shell:

ksh -c 'printf %.5s "${<file;}"'
Run Code Online (Sandbox Code Playgroud)

Mir*_*anc 8

ksh 不仅使用sfio,而且使用它自己的自定义内存分配器。

尽管如此,我的猜测是 sfio 在这种情况下有所作为。我只是尝试在 strace 下运行您的示例,可以看到 ksh 调用读/写约 200 次(65 KB 块),而 sed 调用约 3400 次(4 KB 块)。使用 sed -u 我的笔记本电脑几乎融化了,读取按字节完成,按行写入。Ksh simple 使用 lseek。Grep 使用读取约 400 次(32 KB 块)。