Как отобразить определенные строки из текстового файла в 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

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

Dox
источник
Полезно знать, что есть альтернатива последнему аргументу строки, предложенному Деннисом: счетчик строк в качестве второго 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. Надеюсь, это будет полезно для захвата диапазона линий.

newbie13
источник
Тот же ответ, что и serverfault.com/a/641252/140016 . Downvoted.
Охотник на оленей
Это не тот же ответ. Это должно быть быстрее для больших файлов, поскольку оно фактически прерывается после печати последней строки вместо продолжения сканирования файла.
фобичный
0

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

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

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

shardan
источник
0

Вы можете использовать «нл».

nl filename | grep <line_num>
Аджай
источник
0

Например, этот awk будет печатать строки между 20 и 40

awk '{if ((NR> 20) && (NR <40)) print $ 0}' / etc / passwd

Хрвое Шполяр
источник
0

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

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd
Dagelf
источник
0

Perl - король:

perl -ne 'print if ($. == 10000000 .. $. == 10000020)' filename
Петр В. Мёрч
источник