Как отобразить определенные строки из текстового файла в Linux?


86

Я думаю, что все знают полезные утилиты Linux cmd line headи tail. headпозволяет печатать первые X строк файла, tailделает то же самое, но печатает конец файла. Что такое хорошая команда для печати середины файла? что-то вроде middle --start 10000000 --count 20(выведите от 10 000 до 10 000 010 строк).

Я ищу то, что будет эффективно работать с большими файлами. Я пытался, tail -n 10000000 | head 10и это ужасно медленно.


5
возможный дубликат serverfault.com/questions/101900/…
Кайл Брандт

Ответы:


112
sed -n '10000000,10000020p' filename

Вы могли бы ускорить это немного так:

sed -n '10000000,10000020p; 10000021q' filename

В этих командах опция -nвызывает sed«подавление автоматической печати пространства шаблона». Команда p«print [s] текущее пространство шаблона» и qкоманда «Немедленно завершить работу [s] сценария sed без обработки ввода ...» Цитаты взяты со sed manстраницы .

Кстати, ваша команда

tail -n 10000000 filename | head 10

начинается с десятимиллионной строки от конца файла, в то время как ваша «средняя» команда должна начинаться с десятимиллионной строки с начала, что будет эквивалентно:

head -n 10000010 filename | tail 10

Проблема в том, что для несортированных файлов со строками переменной длины любой процесс должен будет проходить через файл, считая новые строки. Там нет никакого способа, чтобы сократить это.

Однако, если файл отсортирован (например, файл журнала с метками времени) или имеет строки фиксированной длины, то вы можете искать в файле, основываясь на позиции байта. В этом примере файл журнала, вы могли бы сделать бинарный поиск в диапазоне времени , как мой сценарий Python здесь * делает. В случае файла с фиксированной длиной записи это действительно просто. Вы просто ищете linelength * linecountсимволы в файле.

* У меня есть намерение опубликовать еще одно обновление этого скрипта. Может быть, я доберусь до этого на днях.


Вот sedверсия Чарльза middleфункции: middle() { local s=$1 c=$2; shift 2; sed -n "$s,$(($s + $c -1))p; $(($s + $c))q" "$@"; }. Он будет обрабатывать несколько файловых аргументов, имена файлов с пробелами и т. Д. Несколько файлов обрабатываются вместе, как если бы они были обработаны таким же образом, sedкак обычно (таким образом, средняя 1000 100 файл1 файл2 будет проходить от конца первого файла до начала второго, если первый имеет менее 1100 строк).
Деннис Уильямсон

Функция в моем предыдущем комментарии может быть вызвана с параметром middle startline count filenameимени файла: middle startline count file1 file2 file3или с несколькими именами файлов: или с перенаправлением: middle startline count < filenameили в трубе: some_command | среднее число начальных строк илиcat file* | middle startline count
Деннис Уильямсон

Не должно ли `в вашей команде sed быть '? Я не могу заставить его работать с backtick, но он отлично работает с одинарной кавычкой.
Ян Хантер

@beanland: Да, это опечатка. Я исправил это. Благодарю.
Деннис Уильямсон

1
@kev: я добавил пояснения к своему ответу.
Деннис Уильямсон

28

Я обнаружил следующее использование sed

sed -n '10000000,+20p'  filename

Надеюсь, это кому-нибудь пригодится!


Полезно знать, что есть альтернатива последнему аргументу строки, предложенному Деннисом: счетчик строк в качестве второго sed -nаргумента, который делает его вполне читабельным.
user3123159

Пример использования: extract_lines(){sed -n "$1,+$2p" <file>}который пишет в стандартный вывод.
user3123159

4

Это мой первый пост здесь! Во всяком случае, это легко. Допустим, вы хотите извлечь строку 8872 из файла с именем file.txt. Вот как вы это делаете:

cat -n file.txt | grep '^ * 8872'

Теперь вопрос в том, чтобы найти 20 строк после этого. Для этого вы делаете

cat -n file.txt | grep -A 20 '^ * 8872'

Для линий вокруг или перед смотрите флаги -B и -C в руководстве grep.


Хотя это технически правильно и интересный способ сделать это для файла разумного размера, мне любопытно, насколько он эффективен при работе с файлами того размера, о котором спрашивает постер.
Дженни Д.

Несколько строк: cat -n file.txt | grep "^ \ s \ + (10 \ | 20 \ | 30) \ s \ +"
Джеффри Найт

cat -n file.txt | grep '^ *1'выведите все строки с 1 справа. Как вывести строку 1 с помощью этой техники? Я знаю, я могу голову -n 1 .... но как использовать grep?
Sean87

1

Ответ Денниса - это путь. Но используя только голову и хвост, под bash:

middle () {head -n $ [$ 1 + $ 2] | хвост -n $ 2; }

Это сканирует первые строки $ 1 + $ 2 дважды, так что это намного хуже, чем ответ Денниса. Но вам не нужно помнить все эти буквы, чтобы использовать его ....


Использование $[...]не рекомендуется, по крайней мере, в Bash. Кроме того, вам не хватает параметра файла.
Деннис Уильямсон

@ Денис: Отсутствует пропущенный параметр: вы должны использовать это на стандартном вводе, согласно middle 10 10 < /var/log/auth.log.
Чарльз Стюарт

1

Используйте следующую команду, чтобы получить определенный диапазон строк

awk 'NR < 1220974{next}1;NR==1513793{exit}' debug.log | tee -a test.log

Здесь debug.log - мой файл, в котором отсутствуют строки, и я использовал его для печати строк с номера строки 1220974 до 1513793 в файл test.log. Надеюсь, это будет полезно для захвата диапазона линий.


Тот же ответ, что и serverfault.com/a/641252/140016 . Downvoted.
Охотник на оленей

Это не тот же ответ. Это должно быть быстрее для больших файлов, поскольку оно фактически прерывается после печати последней строки вместо продолжения сканирования файла.
фобичный

0

Рубиновая версия.

ruby -pe 'next unless $. > 10000000 && $. < 10000020' < filename.txt

Это может быть полезно для кого-то. Решения с «sed», предоставленные Dennis и Dox, очень хороши, даже несмотря на то, что они кажутся быстрее.




0

Если вам известны нумерации строк, скажем, вы хотите получить строки 1, 3 и 5 из файла, скажем / etc / passwd:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.