Каков наилучший способ извлечь сегмент из текстового файла?

13

Что является хорошим способом извлечь, скажем, строки 20 -45 из огромного текстового файла. Не в интерактивном режиме, конечно!

Крис Хуан-Ливер
источник

Ответы:

12

Вы можете попробовать:

cat textfile | head -n 45 | tail -n 26

или же

cat textfile | awk "20 <= NR && NR <= 45" 

Обновить:

Как отметил Махомедалид, catэто не обязательно и немного избыточно, но это делает для чистой, удобочитаемой команды.

Если catвас это беспокоит, лучшим решением будет:

<textfile awk "20 <= NR && NR <= 45"
Стефан
источник
2
awk NR==20,NR==45 textfileтоже работает и легко читается.
Эфимент
Мне больше нравится использование stdin, он имеет некоторую глобальную согласованность с остальной частью nix
Stefan
1
Чтение из аргументов командной строки также согласуется с другими утилитами UNIX, и моей главной целью было продемонстрировать ,оператор диапазона awk .
Эфимент
лол, я имел в виду @adam. но да, мне нравится ваше предложение
Стефан
Я думаю, что ответ @ ephemient здесь самый лучший. В противном случае команды довольно загадочны.
Лео Леопольд Герц 준영
13

Еще проще:

sed -n '20,45p;45q' < textfile

Флаг -n отключает вывод по умолчанию. «20,45» адресует строки с 20 по 45 включительно. Команда "p" печатает текущую строку. И q выходит после печати строки.

dkagedal
источник
1
+1 приятно, мне нравится, но его строка 20 на 45 :)
Стефан
1
хорошо, хорошо, я отредактировал это, чтобы сказать 20,45 :-)
dkagedal
Удаление qкоманды (все, начиная с ;) улучшило производительность при извлечении одной строки 26995107 из файла 27169334 строки.
Руслан
6

Это не ответ, но я не могу опубликовать его как комментарий.

Другой (очень быстрый) способ сделать это предложил mikeserv здесь :

{ head -n 19 >/dev/null; head -n 26; } <infile

Используя тот же тестовый файл, что и здесь, и ту же процедуру, вот несколько тестов (извлечение строк 1000020-1000045):

mikeserv :

{ head -n 1000019 >/dev/null; head -n 26; } <iplist

real    0m0.059s

Стефан :

head iplist -n 1000045 | tail -n 26

real    0m0.054s

Это, безусловно, самые быстрые решения, и различия незначительны (для одного прохода) (я пробовал с разными диапазонами: пара строк, миллионы строк и т. Д.).

Однако выполнение этого без конвейера может принести существенное преимущество приложению, которому необходимо выполнить поиск по нескольким диапазонам линий аналогичным образом, например:

for  pass in 0 1 2 3 4 5 6 7 8 9
do   printf "pass#$pass:\t"
     head -n99 >&3; head -n1
done <<1000LINES 3>/dev/null
$(seq 1000)
1000LINES

... который печатает ...

pass#0: 100
pass#1: 200
pass#2: 300
pass#3: 400
pass#4: 500
pass#5: 600
pass#6: 700
pass#7: 800
pass#8: 900
pass#9: 1000

... и читает файл только один раз.


Другие sed/ awk/ perlрешения читают весь файл, и поскольку речь идет об огромных файлах, они не очень эффективны. Я добавил несколько вариантов, которые exitили quit после последней строки в указанном диапазоне:

Стефан :

awk "1000020 <= NR && NR <= 1000045" iplist

real    0m2.448s

против

awk "NR >= 1000020;NR==1000045{exit}" iplist

real    0m0.243s

dkagedal ( sed):

sed -n 1000020,1000045p iplist

real    0m0.947s

против

sed '1,1000019d;1000045q' iplist

real    0m0.143s

Стивен Д :

perl -ne 'print if 1000020..1000045' iplist

real    0m2.041s

против

perl -ne 'print if $. >= 1000020; exit if $. >= 1000045;' iplist

real    0m0.369s
don_crissti
источник
+1 Я думаю, что это лучший ответ здесь! Было бы неплохо узнать, сколько времени занимает это awk NR==1000020,NR==1000045 textfileв вашей системе.
Лео Леопольд Герц 준영
3
ruby -ne 'print if 20 .. 45' file
user1606
источник
1
коллега-рубин, вы получите мой голос, сэр
Стефан
1
Пока мы на этом, почему бы и нет python -c 'import fileinput, sys; [sys.stdout.write(line) for nr, line in enumerate(fileinput.input()) if 19 <= nr <= 44]'? :-P Это то, что Ruby, созданный по образцу Perl, вдохновленный awk / sed, может сделать легко.
Эфимент
2

Поскольку sed и awk уже были заняты, вот решение perl:

perl -nle "print if ($. > 19 && $. < 46)" < textfile

Или, как указано в комментариях:

perl -ne 'print if 20..45' textfile
Стивен Д
источник
2
Что со всеми этими дополнительными персонажами? Нет необходимости удалять и повторно добавлять новые строки, триггер предполагает сравнение с номером строки, и оператор Diamond выполняет аргументы, если они предоставлены. perl -ne'print if 20..45' textfile
Эфимент
1
Ницца. Я полагаю, что это немного рефлекс, а в остальном у меня нет оправданий, кроме невежества.
Стивен Д.