有没有一种“规范”的方法?我一直在使用head-n|tail-1来完成这个任务,但我一直在想是否有一个Bash工具专门从文件中提取一行(或一系列行)。

我所说的“规范”是指一个主要功能就是这样做的程序。


当前回答

获取第n行(单行)

如果您想要一些以后可以自定义而不必处理bash的东西,可以编译这个c程序,并将二进制文件放到您的自定义二进制文件目录中。这假设您知道如何编辑.bashrc文件相应地(仅当您想要编辑路径变量时),如果您不知道,这是一个有用的链接。

要运行此代码,请使用(假设您将二进制代码命名为“行”)。

line [target line] [target file]

实例

line 2 somefile.txt

代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char* argv[]){

  if(argc != 3){
      fprintf(stderr, "line needs a line number and a file name");
      exit(0);     
  }

  int lineNumber = atoi(argv[1]); 
  int counter = 0; 
  char *fileName = argv[2];

  FILE *fileReader = fopen(fileName, "r");
  if(fileReader == NULL){
      fprintf(stderr, "Failed to open file"); 
      exit(0); 
  }

  size_t lineSize = 0;
  char* line = NULL;

  while(counter < lineNumber){
     getline(&line, &linesize, fileReader);
     counter++
  }

  getline(&line, &lineSize, fileReader);

  printf("%s\n", line);     

  fclose(fileReader); 
  return 0; 
}

EDIT:删除fseek并用while循环替换它

其他回答

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

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

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

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

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

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

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

然后用tail读取,它实际上直接查找文件中的适当点!

例如获得线1000:

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

这可能不适用于2字节/多字节字符,因为awk是“字符识别”的,但tail不是。我还没有对一个大文件进行测试。另请参阅此答案。或者,将文件拆分为更小的文件!

作为CaffeineConnisseur非常有用的基准测试答案的后续。。。我很好奇“mapfile”方法与其他方法相比的速度有多快(因为没有测试),所以我自己尝试了一个快速而肮脏的速度比较,因为我手边有bash 4。在我做这项测试时,我在顶部答案的一条评论中提到了“tail|head”方法(而不是head|tail),因为人们都在称赞它。我没有使用的测试文件的大小;我能在短时间内找到的最好的文件是一个14M的谱系文件(用空格分隔的长行,略低于12000行)。

短版本:mapfile看起来比cut方法快,但比其他任何方法都慢,所以我称它为无用的。tail|head,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

希望这有帮助!

要使用sed和变量作为行号打印第n行,请执行以下操作:

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

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

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

sed 'NUMq;d' file

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

#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;
}