ChatGPT解决这个技术问题 Extra ChatGPT

Bash工具从文件中获取第n行

有这样做的“规范”方式吗?我一直在使用 head -n | tail -1 来解决问题,但我一直想知道是否有一个 Bash 工具可以专门从文件中提取一行(或一系列行)。

“规范”是指其主要功能正在执行此操作的程序。

“Unix 方式”是将能够很好地完成各自工作的工具链接起来。所以我想你已经找到了一个非常合适的方法。其他方法包括 awksed,我相信有人也能想出一个 Perl 单行器;)
双重命令表明 head | tail 解决方案是次优的。已经提出了其他更接近最佳的解决方案。
您是否针对平均情况下最快的解决方案进行了任何基准测试?
Unix & Linuxcat line X to line Y on a huge file 的基准(范围)。 (cc @Marcin,以防两年多后您仍然想知道)
如果您查询输入中不存在的行,则 head | tail 解决方案不起作用:它将打印最后一行。

t
tripleee

对于大文件,head 和带有 tail 的管道会很慢。我会建议 sed 像这样:

sed 'NUMq;d' file

其中 NUM 是您要打印的行号;例如,sed '10q;d' file 打印 file 的第 10 行。

解释:

当行号为 NUM 时,NUMq 将立即退出。

d 将删除该行而不是打印它;这在最后一行被禁止,因为 q 导致退出时跳过脚本的其余部分。

如果变量中有 NUM,则需要使用双引号而不是单引号:

sed "${NUM}q;d" file

对于那些想知道的人,这个解决方案似乎比下面提出的 sed -n 'NUMp'sed 'NUM!d' 解决方案快 6 到 9 倍。
我认为 tail -n+NUM file | head -n1 可能同样快或更快。至少,当我在一个有 50 万行的文件上尝试 NUM 为 250000 时,它在我的系统上(显着)更快。 YMMV,但我真的不明白为什么会这样。
不它不是。如果没有 q,它将处理完整文件
@Fiddlestiques:不要忘记引用使其foo="$(sed "4q;d" file4)"
@anubhava - 谢谢 - 现在知道了 - echo "$foo" 而不是 echo $foo
C
Community
sed -n '2p' < file.txt

将打印第二行

sed -n '2011p' < file.txt

2011年线

sed -n '10,33p' < file.txt

第 10 行至第 33 行

sed -n '1p;3p' < file.txt

1号线和3号线

等等...

要使用 sed 添加行,您可以检查以下内容:

sed: insert a line in a certain position


为什么在这种情况下需要'<'?如果没有它,我不会获得相同的输出吗?
@RafaelBarbosa < 在这种情况下是不必要的。简单地说,我更喜欢使用重定向,因为我经常使用像 sed -n '100p' < <(some_command) 这样的重定向 - 所以,通用语法:)。它的效果并不差,因为在分叉时使用 shell 完成重定向,所以......这只是一种偏好......(是的,它长了一个字符):)
@jm666 实际上它长了 2 个字符,因为您通常会在 < 之后放置 '<' 以及一个额外的空格 ' ',如果你没有使用 < :)
@rasen58 空间也是一个字符? :) /好吧,开个玩笑-你说得对/ :)
读取 50M 行的文件时,这比尾部/头部组合慢约 5 倍
P
Peter Mortensen

我有一个独特的情况,我可以对这个页面上提出的解决方案进行基准测试,所以我写这个答案是对所提出的解决方案的整合,每个解决方案都包含运行时间。

设置

我有一个 3.261 GB 的 ASCII 文本数据文件,每行有一个键值对。该文件总共包含 3,339,550,320 行,并且无法在我尝试过的任何编辑器中打开,包括我的首选 Vim。我需要对这个文件进行子集化,以调查我发现的一些值仅从 ~500,000,000 行开始。

因为文件有这么多行:

我只需要提取行的一个子集来对数据做任何有用的事情。

通读我关心的价值观的每一行都需要很长时间。

如果解决方案读取了我关心的行并继续读取文件的其余部分,它将浪费时间读取近 30 亿个不相关的行,并且花费的时间比必要的长 6 倍。

我最好的情况是一个解决方案,它只从文件中提取一行而不读取文件中的任何其他行,但我想不出我将如何在 Bash 中完成此操作。

为了我的理智,我不会试图阅读我自己的问题需要的全部 500,000,000 行。相反,我将尝试从 3,339,550,320 行中提取第 50,000,000 行(这意味着读取完整文件所需的时间比必要的长 60 倍)。

我将使用内置的 time 对每个命令进行基准测试。

基线

首先让我们看看head tail 解决方案如何:

$ time head -50000000 myfile.ascii | tail -1
pgm_icnt = 0

real    1m15.321s

第 5000 万行的基线是 00:01:15.321,如果我直奔第 50000 万行,大概需要 12.5 分钟。

我对此表示怀疑,但值得一试:

$ time cut -f50000000 -d$'\n' myfile.ascii
pgm_icnt = 0

real    5m12.156s

这个跑了 00:05:12.156,比基线慢很多!我不确定它是在停止之前读取整个文件还是仅读取 5000 万行,但无论如何,这似乎不是解决问题的可行解决方案。

AWK

我只使用 exit 运行解决方案,因为我不会等待完整文件运行:

$ time awk 'NR == 50000000 {print; exit}' myfile.ascii
pgm_icnt = 0

real    1m16.583s

此代码在 00:01:16.583 中运行,仅慢了约 1 秒,但在基线上仍然没有改进。按照这个速度,如果排除了退出命令,可能需要大约 76 分钟才能读取整个文件!

Perl

我也运行了现有的 Perl 解决方案:

$ time perl -wnl -e '$.== 50000000 && print && exit;' myfile.ascii
pgm_icnt = 0

real    1m13.146s

此代码在 00:01:13.146 中运行,比基线快约 2 秒。如果我在全部 500,000,000 上运行它,可能需要大约 12 分钟。

sed

板上的最佳答案,这是我的结果:

$ time sed "50000000q;d" myfile.ascii
pgm_icnt = 0

real    1m12.705s

此代码在 00:01:12.705 中运行,比基线快 3 秒,比 Perl 快约 0.4 秒。如果我在完整的 500,000,000 行上运行它可能需要大约 12 分钟。

地图文件

我有 bash 3.1,因此无法测试 mapfile 解决方案。

结论

看起来,在大多数情况下,很难改进 head tail 解决方案。 sed 解决方案最多可将效率提高约 3%。

(使用公式 % = (runtime/baseline - 1) * 100 计算的百分比)

第 50,000,000 行

00:01:12.705 (-00:00:02.616 = -3.47%) sed 00:01:13.146 (-00:00:02.175 = -2.89%) perl 00:01:15.321 (+00:00:00.000 = + 0.00%) head|tail 00:01:16.583 (+00:00:01.262 = +1.68%) awk 00:05:12.156 (+00:03:56.835 = +314.43%) 削减

行 500,000,000

00:12:07.050 (-00:00:26.160) sed 00:12:11.460 (-00:00:21.750) perl 00:12:33.210 (+00:00:00.000) 头|尾 00:12:45.830 ( +00:00:12.620) awk 00:52:01.560 (+00:40:31.650) 切

第 3,338,559,320 行

01:20:54.599 (-00:03:05.327) sed 01:21:24.045 (-00:02:25.227) perl 01:23:49.273 (+00:00:00.000) 头|尾 01:25:13.548 ( +00:02:35.735) awk 05:47:23.026 (+04:24:26.246) 切


我想知道将整个文件放入 /dev/null 需要多长时间。 (如果这只是一个硬盘基准测试呢?)
我有一种不正当的冲动,要向您拥有 3 个以上的 gig 文本文件字典鞠躬。无论是什么原理,这都包含文本性:)
对于单个文件,使用 head + tail 运行两个进程的开销可以忽略不计,但当您对多个文件执行此操作时就会开始显示。
f
fedorqui

使用 awk 非常快:

awk 'NR == num_line' file

如果为真,则执行 awk 的默认行为:{print $0}

替代版本

如果您的文件碰巧很大,您最好在阅读所需的行后exit。这样可以节省 CPU 时间请参阅答案末尾的时间比较

awk 'NR == num_line {print; exit}' file

如果要从 bash 变量中给出行号,可以使用:

awk 'NR == n' n=$num file
awk -v n=$num 'NR == n' file   # equivalent

查看使用 exit 节省了多少时间,特别是如果该行恰好位于文件的第一部分:

# Let's create a 10M lines file
for ((i=0; i<100000; i++)); do echo "bla bla"; done > 100Klines
for ((i=0; i<100; i++)); do cat 100Klines; done > 10Mlines

$ time awk 'NR == 1234567 {print}' 10Mlines
bla bla

real    0m1.303s
user    0m1.246s
sys 0m0.042s
$ time awk 'NR == 1234567 {print; exit}' 10Mlines
bla bla

real    0m0.198s
user    0m0.178s
sys 0m0.013s

所以差异是 0.198 秒和 1.303 秒,快了大约 6 倍。


这种方法总是会比较慢,因为 awk 会尝试进行字段拆分。字段拆分的开销可以减少 awk 'BEGIN{FS=RS}(NR == num_line) {print; exit}' file
当您想要连接 file1 的 n1 行、file2 的 n2 行、n3 或 file3 ... awk 'FNR==n' n=10 file1 n=30 file2 n=60 file3 时,这种方法中 awk 的真正威力就显现出来了。使用 GNU awk 可以使用 awk 'FNR==n{print;nextfile}' n=10 file1 n=30 file2 n=60 file3 加速。
@kvantour 确实,GNU awk 的 nextfile 非常适合此类事情。 FS=RS 如何避免字段拆分?
FS=RS并没有避免字段拆分,而是只解析$0的,只分配一个字段,因为$0中没有RS
@fedorqui 我为你感到高兴
P
Philipp Claßen

根据我的测试,在性能和可读性方面,我的建议是:

tail -n+N | head -1

N 是您想要的行号。例如,tail -n+7 input.txt | head -1 将打印文件的第 7 行。

tail -n+N 将打印从第 N 行开始的所有内容,head -1 将使其在一行后停止。

替代的 head -N | tail -1 可能更具可读性。例如,这将打印第 7 行:

head -7 input.txt | tail -1

就性能而言,较小的尺寸没有太大差异,但当文件变大时,它会被 tail | head(上图)所超越。

票数最高的 sed 'NUMq;d' 很有趣,但我认为与 head/tail 解决方案相比,开箱即用的人会更少,而且它也比 tail/head 慢。

在我的测试中,两个尾部/头部版本的性能始终优于 sed 'NUMq;d'。这与发布的其他基准一致。很难找到反面/正面非常糟糕的情况。这也不足为奇,因为这些是您期望在现代 Unix 系统中进行大量优化的操作。

为了了解性能差异,这些是我为一个大文件(9.3G)得到的数字:

尾-n+N |头 -1:3.7 秒

头-N |尾巴 -1:4.6 秒

sed Nq;d: 18.8 秒

结果可能会有所不同,但一般而言,head | tailtail | head 的性能对于较小的输入具有可比性,并且 sed 总是慢一个重要因素(大约 5 倍左右)。

要重现我的基准,您可以尝试以下操作,但请注意它将在当前工作目录中创建一个 9.3G 文件:

#!/bin/bash
readonly file=tmp-input.txt
readonly size=1000000000
readonly pos=500000000
readonly retries=3

seq 1 $size > $file
echo "*** head -N | tail -1 ***"
for i in $(seq 1 $retries) ; do
    time head "-$pos" $file | tail -1
done
echo "-------------------------"
echo
echo "*** tail -n+N | head -1 ***"
echo

seq 1 $size > $file
ls -alhg $file
for i in $(seq 1 $retries) ; do
    time tail -n+$pos $file | head -1
done
echo "-------------------------"
echo
echo "*** sed Nq;d ***"
echo

seq 1 $size > $file
ls -alhg $file
for i in $(seq 1 $retries) ; do
    time sed $pos'q;d' $file
done
/bin/rm $file

这是在我的机器上运行的输出(带有 SSD 和 16G 内存的 ThinkPad X1 Carbon)。我假设在最终运行中,所有内容都将来自缓存,而不是来自磁盘:

*** head -N | tail -1 ***
500000000

real    0m9,800s
user    0m7,328s
sys     0m4,081s
500000000

real    0m4,231s
user    0m5,415s
sys     0m2,789s
500000000

real    0m4,636s
user    0m5,935s
sys     0m2,684s
-------------------------

*** tail -n+N | head -1 ***

-rw-r--r-- 1 phil 9,3G Jan 19 19:49 tmp-input.txt
500000000

real    0m6,452s
user    0m3,367s
sys     0m1,498s
500000000

real    0m3,890s
user    0m2,921s
sys     0m0,952s
500000000

real    0m3,763s
user    0m3,004s
sys     0m0,760s
-------------------------

*** sed Nq;d ***

-rw-r--r-- 1 phil 9,3G Jan 19 19:50 tmp-input.txt
500000000

real    0m23,675s
user    0m21,557s
sys     0m1,523s
500000000

real    0m20,328s
user    0m18,971s
sys     0m1,308s
500000000

real    0m19,835s
user    0m18,830s
sys     0m1,004s

head | tailtail | head 的性能是否不同?还是取决于正在打印哪一行(文件开头与文件结尾)?
@wisbucky我没有确切的数字,但是首先使用tail后跟“head -1”的一个缺点是您需要提前知道总长度。如果您不知道,则必须先计算它,这将在性能方面有所损失。另一个缺点是使用起来不太直观。例如,如果您有数字 1 到 10 并且想要获得第 3 行,则必须使用“tail -8 | head -1”。这比“head -3 | tail -1”更容易出错。
对不起,我应该包括一个例子来清楚。 head -5 | tail -1tail -n+5 | head -1。实际上,我找到了另一个进行测试比较的答案,发现 tail | head 更快。 stackoverflow.com/a/48189289
@wisbucky 谢谢你提到它!我做了一些测试,不得不同意它总是稍微快一点,与我所看到的线的位置无关。鉴于此,我改变了我的答案,并且还包括了基准,以防有人想要复制它。
有没有一种简单的方法可以一次将此解决方案扩展到多个文件?例如 head -7 -q input*.txt | tail -1 从多个文件 input*.txt 中获取第 7 行?目前这只会从 input*.txt 中列出的第一个文件中获取第 7 行。
K
Kevin

哇,所有的可能性!

尝试这个:

sed -n "${lineNum}p" $file

或其中之一,取决于您的 Awk 版本:

awk  -vlineNum=$lineNum 'NR == lineNum {print $0}' $file
awk -v lineNum=4 '{if (NR == lineNum) {print $0}}' $file
awk '{if (NR == lineNum) {print $0}}' lineNum=$lineNum $file

您可能必须尝试 nawkgawk 命令)。

有没有只打印特定行的工具?不是标准工具之一。但是,sed 可能是最接近且最容易使用的。


g
gniourf_gniourf

这个问题被标记为 Bash,这是 Bash (≥4) 的做法:将 mapfile-s(跳过)和 -n(计数)选项一起使用。

如果您需要获取文件 file 的第 42 行:

mapfile -s 41 -n 1 ary < file

此时,您将拥有一个数组 ary,其中的字段包含 file 的行(包括尾随换行符),我们跳过了前 41 行(-s 41),并在读取一个后停止行 (-n 1)。所以这真的是第 42 行。打印出来:

printf '%s' "${ary[0]}"

如果您需要一系列行,请说范围 42–666(含),并说您不想自己做数学运算,并将它们打印在标准输出上:

mapfile -s $((42-1)) -n $((666-42+1)) ary < file
printf '%s' "${ary[@]}"

如果您也需要处理这些行,那么存储尾随换行符并不是很方便。在这种情况下,使用 -t 选项(修剪):

mapfile -t -s $((42-1)) -n $((666-42+1)) ary < file
# do stuff
printf '%s\n' "${ary[@]}"

您可以使用一个函数为您执行此操作:

print_file_range() {
    # $1-$2 is the range of file $3 to be printed to stdout
    local ary
    mapfile -s $(($1-1)) -n $(($2-$1+1)) ary < "$3"
    printf '%s' "${ary[@]}"
}

没有外部命令,只有 Bash 内置命令!


S
Saurav Sahu

保存两次击键,不使用括号打印第 N 行:

sed  -n  Np  <fileName>
      ^   ^
       \   \___ 'p' for printing
        \______ '-n' for not printing by default 

例如,要打印第 100 行:

sed -n 100p foo.txt      

请注意,第一行的 N = 1 而不是零。
Z
Zombo

您也可以使用 sed 打印并退出:

sed -n '10{p;q;}' file   # print line 10

-n 选项禁用打印每一行的默认操作,您肯定会通过快速浏览手册页发现这一点。
GNU sed 中,所有 sed 答案的速度都差不多。因此(对于 GNU sed)这是最好的 sed 答案,因为它可以为大文件和小 nth line 值节省时间。
T
Toby

您也可以为此使用 Perl:

perl -wnl -e '$.== NUM && print && exit;' some.file

在对包含 6,000,000 行的文件进行测试并检索任意行 #2,000,000 时,此命令几乎是即时的,并且比 sed 答案快得多。
J
Jo Valentine-Cooper

作为 CaffeineConnoisseur 非常有用的基准测试答案的后续行动......我很好奇“mapfile”方法与其他方法相比有多快(因为未经测试),所以我自己尝试了一个快速而肮脏的速度比较我确实有 bash 4。在我对最佳答案的评论之一中提到的“tail | head”方法(而不是head | tail)中进行了测试,因为人们正在对其赞不绝口。我没有任何接近使用的测试文件大小的东西;我能在短时间内找到的最好的文件是一个 14M 的谱系文件(用空格分隔的长行,不到 12000 行)。

简短版本:mapfile 看起来比 cut 方法快,但比其他所有方法都慢,所以我称之为哑巴。尾巴 |头,OTOH,看起来它可能是最快的,尽管与 sed 相比,对于这种大小的文件,差异并不是那么大。

$ time head -11000 [filename] | tail -1
[output redacted]

real    0m0.117s

$ time cut -f11000 -d$'\n' [filename]
[output redacted]

real    0m1.081s

$ time awk 'NR == 11000 {print; exit}' [filename]
[output redacted]

real    0m0.058s

$ time perl -wnl -e '$.== 11000 && print && exit;' [filename]
[output redacted]

real    0m0.085s

$ time sed "11000q;d" [filename]
[output redacted]

real    0m0.031s

$ time (mapfile -s 11000 -n 1 ary < [filename]; echo ${ary[0]})
[output redacted]

real    0m0.309s

$ time tail -n+11000 [filename] | head -n1
[output redacted]

real    0m0.028s

希望这可以帮助!


C
Community

大文件的最快解决方案总是tail|head,前提是两个距离:

从文件的开头到起始行。让我们称之为S

从最后一行到文件末尾的距离。不管是E

是已知的。然后,我们可以使用这个:

mycount="$E"; (( E > S )) && mycount="+$S"
howmany="$(( endline - startline + 1 ))"
tail -n "$mycount"| head -n "$howmany"

多少只是所需的行数。

https://unix.stackexchange.com/a/216614/79743 中有更多详细信息


请阐明SE 的单位(即字节、字符或行)。
S
Sanjay Manohar

以上所有答案都直接回答了这个问题。但这是一个不太直接的解决方案,但可能是一个更重要的想法,可以引发思考。

由于行长是任意的,因此需要读取文件第 n 行之前的所有字节。如果您有一个巨大的文件或需要多次重复此任务,并且此过程很耗时,那么您应该首先认真考虑是否应该以不同的方式存储数据。

真正的解决方案是有一个索引,例如在文件的开头,指示行开始的位置。您可以使用数据库格式,或者只是在文件开头添加一个表。或者,创建一个单独的索引文件来伴随您的大文本文件。

例如,您可以为换行创建一个字符位置列表:

awk 'BEGIN{c=0;print(c)}{c+=length()+1;print(c+1)}' file.txt > file.idx

然后用 tail 读取,实际上 seek 直接指向文件中的相应点!

例如获取第 1000 行:

tail -c +$(awk 'NR=1000' file.idx) file.txt | head -1

这可能不适用于 2 字节/多字节字符,因为 awk 是“字符感知”的,但 tail 不是。

我还没有针对大文件对此进行测试。

另请参阅此答案。

或者 - 将您的文件拆分成更小的文件!


M
Mark Shust at M.academy

使用其他人提到的内容,我希望这是我的 bash shell 中的一个快速且花哨的功能。

创建文件:~/.functions

向其中添加内容:

getline() { line=$1 sed $line'q;d' $2 }

然后将其添加到您的 ~/.bash_profile

source ~/.functions

现在,当您打开一个新的 bash 窗口时,您可以像这样调用该函数:

getline 441 myfile.txt


在使用之前无需将 $1 分配给另一个变量,并且您正在破坏任何其他全局 line。在 Bash 中,对函数变量使用 local;但在这里,如前所述,可能只做sed "$1d;q" "$2"。 (另请注意 "$2" 的引用。)
正确,但拥有自记录代码可能会有所帮助。
M
Melroy van den Berg

如果你得到多行由 \n 分隔(通常是新行)。您也可以使用“剪切”:

echo "$data" | cut -f2 -d$'\n'

您将从文件中获得第二行。 -f3 为您提供第三行。


也可用于显示多行:cat FILE | cut -f2,5 -d$'\n' 将显示 FILE 的第 2 行和第 5 行。 (但它不会保留顺序。)
t
tripleee

已经有很多好的答案了。我个人选择awk。为方便起见,如果您使用 bash,只需将以下内容添加到您的 ~/.bash_profile。并且,下次您登录时(或者如果您在此更新后获取 .bash_profile),您将有一个新的漂亮的“nth”功能可用于通过管道传输您的文件。

执行此操作或将其放入您的 ~/.bash_profile(如果使用 bash)并重新打开 bash(或执行 source ~/.bach_profile

# print just the nth piped in line
nth () { awk -vlnum=${1} 'NR==lnum {print; exit}'; } 

然后,要使用它,只需通过管道即可。例如,:

$ yes line | cat -n | nth 5
     5  line

a
aliasav

使用 sed 以变量作为行号打印第 n 行:

a=4
sed -e $a'q:d' file

这里的“-e”标志用于将脚本添加到要执行的命令中。


冒号是语法错误,应该是分号。
U
Ulysse BN

查看 the top answerthe benchmark 后,我实现了一个小帮助函数:

function nth {
    if (( ${#} < 1 || ${#} > 2 )); then
        echo -e "usage: $0 \e[4mline\e[0m [\e[4mfile\e[0m]"
        return 1
    fi
    if (( ${#} > 1 )); then
        sed "$1q;d" $2
    else
        sed "$1q;d"
    fi
}

基本上你可以以两种方式使用它:

nth 42 myfile.txt
do_stuff | nth 42

t
tripleee

我已将上述一些答案放入一个简短的 bash 脚本中,您可以将其放入名为 get.sh 的文件中并链接到 /usr/local/bin/get(或您喜欢的任何其他名称)。

#!/bin/bash
if [ "${1}" == "" ]; then
    echo "error: blank line number";
    exit 1
fi
re='^[0-9]+$'
if ! [[ $1 =~ $re ]] ; then
    echo "error: line number arg not a number";
    exit 1
fi
if [ "${2}" == "" ]; then
    echo "error: blank file name";
    exit 1
fi
sed "${1}q;d" $2;
exit 0

确保它是可执行的

$ chmod +x get

链接它以使其在 PATH 上可用

$ ln -s get.sh /usr/local/bin/get

B
Boris Verkhovskiy

使用 head -n <1-indexed line number you want> 获取前 n 行,然后使用 tail -1 获取最后一行:

echo "$data" | head -n 50 | tail -1

如果您的文件少于 n 行,这将返回最后一行。


J
Jedi Knight

这不是 bash 解决方案,但我发现顶级选择不能满足我的需求,例如,

sed 'NUMq;d' file

速度足够快,但挂了几个小时,并没有说明任何进展。我建议编译这个 cpp 程序并使用它来找到你想要的行。您可以使用 g++ main.cpp 编译它,其中 main.cpp 是包含以下内容的文件。我得到了 a.out 并用 ./a.out 执行了它

#include <iostream>
#include <string>
#include <fstream>

using namespace std;

int main() {
    string filename;
    cout << "Enter filename ";
    cin >> filename;

    int needed_row_number;
    cout << "Enter row number ";
    cin >> needed_row_number;

    int progress_line_count;
    cout << "Enter at which every number of rows to monitor progress ";
    cin >> progress_line_count;

    char ch;
    int row_counter = 1;
    fstream fin(filename, fstream::in);
    while (fin >> noskipws >> ch) {
        int ch_int = (int) ch;
        if (row_counter == needed_row_number) {
            cout << ch;
        }
        if (ch_int == 10) {
            if (row_counter == needed_row_number) {
                return 0;
            }
            row_counter++;
            if (row_counter % progress_line_count == 0) {
                cout << "Progress: line " << row_counter << endl;
            }
        }

    }
    return 0;
}

R
RARE Kpop Manifesto

我想反驳 perlawk 快的概念:

因此,虽然我的测试文件没有那么多行,但它的大小也是 7.58 GB 的两倍 -

我什至给了 perl 一些内置的优势——比如在行号中进行硬编码,并且排在第二位,从而从操作系统缓存机制中获得任何潜在的加速(如果有的话)

 f="$( grealpath -ePq  ~/master_primelist_18a.txt )"
 rownum='133668997'
 fg;fg; pv < "${f}" | gwc -lcm 
 echo; sleep 2; 
 echo; 
 ( time ( pv -i 0.1 -cN in0 < "${f}" | 
        
    LC_ALL=C mawk2 '_{exit}_=NR==+__' FS='^$' __="${rownum}" 

 ) ) | mawk 'BEGIN { print } END { print _ } NR' 
 sleep 2
 ( time ( pv -i 0.1 -cN in0 < "${f}" | 

    LC_ALL=C perl -wnl -e '$.== 133668997 && print && exit;' 

 ) ) | mawk 'BEGIN { print }  END { print _ } NR' ;

fg: no current job
fg: no current job
7.58GiB 0:00:28 [ 275MiB/s] [============>] 100%
        
148,110,134 8,134,435,629 8,134,435,629   <<<< rows, chars, and bytes 
                                               count as reported by gnu-wc



      in0: 5.45GiB 0:00:07 [ 701MiB/s] [=> ] 71%            
( pv -i 0.1 -cN in0 < "${f}" | LC_ALL=C mawk2 '_{exit}_=NR==+__' FS='^$' ; )  
   6.22s user 2.56s system 110% cpu 7.966 total
   77.37219=195591955519519519559551=0x296B0FA7D668C4A64F7F=


      in0: 5.45GiB 0:00:17 [ 328MiB/s] [=> ] 71%            
( pv -i 0.1 -cN in0 < "${f}" | LC_ALL=C perl -wnl -e ; )  
   14.22s user 3.31s system 103% cpu 17.014 total
   77.37219=195591955519519519559551=0x296B0FA7D668C4A64F7F=

如果您认为它会有所作为(也没有安装),我可以使用 perl 5.36 甚至 perl-6 重新运行测试,但差距为

7.966 secs (mawk2)17.014 secs (perl 5.34)

在两者之间,后者是先前的两倍多,似乎很清楚哪一个在 ASCII 文件中获取单行深度确实更快。

This is perl 5, version 34, subversion 0 (v5.34.0) built for darwin-thread-multi-2level

Copyright 1987-2021, Larry Wall


mawk 1.9.9.6, 21 Aug 2016, Copyright Michael D. Brennan