协慌网

登录 贡献 社区

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

有没有一种 “规范” 的方式来做到这一点?我一直在使用head -n | tail -1可以达到目的,但是我一直在想是否有一个 Bash 工具专门从文件中提取一行(或一系列行)。

“规范” 是指一个程序,其主要功能就是这样做。

答案

headtail大的文件会很慢。我建议sed这样:

sed 'NUMq;d' file

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

解释:

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

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

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

sed "${NUM}q;d" file
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

第一和第三行

等等...

要添加带有 sed 的行,您可以检查以下内容:

sed:在特定位置插入一行

我有一个独特的情况,可以在此页面上对提出的解决方案进行基准测试,因此我将这个答案写成对提出的解决方案的合并,其中包括每个解决方案的运行时间。

设置

我有一个 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,如果我直接进入 5 亿行,则可能需要 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 解决方案:

$ 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 行

  1. 00:01:12.705(-00:00:02.616 = -3.47%) sed
  2. 00:01:13.146(-00:00:02.175 = -2.89%) perl
  3. 00:01:15.321(+00:00:00.000 = + 0.00%) head|tail
  4. 00:01:16.583(+00:00:01.262 = + 1.68%) awk
  5. 00:05:12.156(00:03:56.835 = + 314.43%) cut

第 500,000,000 行

  1. 00:12:07.050(-00:00:26.160) sed
  2. 00:12:11.460(-00:00:21.750) perl
  3. 00:12:33.210(+00:00:00.000) head|tail
  4. 00:12:45.830(+00:00:12.620) awk
  5. 00:52:01.560(+00:40:31.650) cut

第 3,338,559,320 行

  1. 01:20:54.599(-00:03:05.327) sed
  2. 01:21:24.045(-00:02:25.227) perl
  3. 01:23:49.273(+00:00:00.000) head|tail
  4. 01:25:13.548(+00:02:35.735) awk
  5. 05:47:23.026(+04:24:26.246) cut