grep -n | sort | sed | cut
( export LC_ALL=C
grep -n '' | sort -t: -nmk1,1 ./L - |
sed /:/d\;n | cut -sd: -f2-
) <./F
无论输入什么大小,它都应该可以很快地工作(下面包括一些定时测试)。有关如何的一些注意事项:
export LC_ALL=C
- 因为以下操作的重点是使整个文件
./F
与其./L
lineno的文件内联,所以我们真正需要担心的唯一字符是ASCII [0-9]
数字和:
冒号。
- 因此,担心是否要在一组128种可能的字符中找到这11个字符比使用UTF-8时要容易得多。
grep -n ''
- 这会将字符串
LINENO:
插入到stdin-或中的每一行的开头<./F
。
sort -t: -nmk1,1 ./L -
sort
根本不考虑对其输入文件进行排序,而是(正确地)假定它们已-m
预先-numerically
排序并按排序顺序合并它们,而基本上忽略了任何可能-k1,1
出现的-t:
冒号字符。
- 尽管这可能需要一些临时空间(取决于某些序列可能发生的间隔),但与适当的排序相比并不需要太多,并且它非常快,因为它涉及零回溯。
sort
将输出单个流,其中任何lineno都./L
将紧接在中的相应行之前./F
。./L
的行始终排在最前面,因为它们较短。
sed /:/d\;n
- 如果当前行与
/:/
冒号匹配,d
则将其从输出中删除。否则,自动打印当前n
行和外部行。
- 因此,将
sed
prunes sort
的输出仅输出到与冒号和下一行不匹配的顺序行对-或仅输出到./L
然后是下一行的行。
cut -sd: -f2-
cut
-s
从输出中压制不包含其至少一个-d:
分隔符字符串之一的输入行-从而./L
完全修剪了的行。
- 对于这些行,它们的第一个
:
冒号分隔的字段-f
就cut
消失了-所有grep
插入的行号也都消失了。
小输入测试
seq 5 | sed -ne'2,3!w /tmp/L
s/.*/a-z &\& 0-9/p' >/tmp/F
...生成5行样本输入。然后...
( export LC_ALL=C; </tmp/F \
grep -n '' | sort -t: -nmk1,1 ./L - |
sed /:/d\;n | cut -sd: -f2-
)| head - /tmp[FL]
...印刷品...
==> standard input <==
a-z 1& 0-9
a-z 4& 0-9
a-z 5& 0-9
==> /tmp/F <==
a-z 1& 0-9
a-z 2& 0-9
a-z 3& 0-9
a-z 4& 0-9
a-z 5& 0-9
==> /tmp/L <==
1
4
5
更大的定时测试
我创建了几个非常大的文件:
seq 5000000 | tee /tmp/F |
sort -R | head -n1500000 |
sort -n >/tmp/L
...将500万行放入其中,并将150万行/tmp/F
随机选择放入/tmp/L
。然后我做了:
time \
( export LC_ALL=C
grep -n '' | sort -t: -nmk1,1 ./L - |
sed /:/d\;n | cut -sd: -f2-
) <./F |wc - l
它打印:
1500000
grep -n '' \
0.82s user 0.05s system 73% cpu 1.185 total
sort -t: -nmk1,1 /tmp/L - \
0.92s user 0.11s system 86% cpu 1.185 total
sed /:/d\;n \
1.02s user 0.14s system 98% cpu 1.185 total
cut -sd: -f2- \
0.79s user 0.17s system 80% cpu 1.184 total
wc -l \
0.05s user 0.07s system 10% cpu 1.183 total
(我在那里添加了反斜杠)
在这里当前提供的解决方案中,这是所有解决方案中最快的,但是与我机器上上面生成的数据集相提并论。在其他人中,只有一个接近争夺第二名,那就是perl
在这里。
这绝不是原始的解决方案-由于其他人的建议/启发,它减少了执行时间的三分之一。有关较慢的解决方案,请参见发布历史记录(但是为什么?)。
另外,值得注意的是,如果不是针对我系统的多CPU体系结构以及该管道中每个进程的并发执行,则其他答案可能会更好。它们都在同一时间工作-每个都在自己的处理器核心上-传递数据并处理整个数据的一小部分。它太酷了。
但是最快的解决方案是...
但这不是最快的解决方案。这里提供的最快的解决方案是C程序。我叫它cselect
。将其复制到X剪贴板后,我将其编译为:
xsel -bo | cc -xc - -o cselect
然后我做了:
time \
./cselect /tmp/L /tmp/F |
wc -l
...结果是...
1500000
./cselect /tmp/L /tmp/F \
0.50s user 0.05s system 99% cpu 0.551 total
wc -l \
0.05s user 0.05s system 19% cpu 0.551 total