Показать все файлы до матча

71

grep --before-context 5 показывает 5 строк перед матчем.

Я хочу показать все перед матчем.
Делать grep --before-context 99999999будет работать, но это не очень ... профессионально.

Как показать все файлы до матча?

Николас Рауль
источник

Ответы:

95

Сед лучше для этого.

Просто сделать:

sed '/PATTERN/q' FILE

Это работает так:

Для каждой строки мы посмотрим, соответствует ли она /PATTERN:

  • если да, мы распечатаем его и выйдем
  • в противном случае мы печатаем это

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

Этот трюк также можно использовать для эмуляции head:

sed 10q FILE
Mikel
источник
Только что попробовал, он просто выводит первую строку файла ... даже если совпадение находится на строке 38.
Николас Рауль
У меня отлично работает. Можете ли вы привести пример реального ввода и вывода? И команда, которую вы запускаете как есть.
Микель
Я пробовал вашу команду до того, как вы ее отредактировали, это было: sed '/ PATTERN / p; q' FILE
Николас Рауль,
7
Что мне делать, если я не хочу печатать строку с шаблоном соответствия?
Tommy.carstensen
4
@ tommy.carstensen: sed '/PATTERN/Q' FILEпропустит согласованную строку. Qэто расширение GNU, поэтому оно не будет работать с любым sed.
Алекс О
37

sed может заменить большую часть функциональности grep.

sed -n '1,/<pattern>/ p' <file>

Это означает печать с первой строки до совпадения шаблона.

Пара примеров из диапазона

sed -n '/<pattern>/,$ p' <file> # from pattern to end of file
sed -n '/<pattern1>/,/<pattern2>/ p' <file> # from pattern1 to pattern2
forcefsck
источник
3
Эта команда хороша, но вы можете сделать лучше. Таким образом, он читает весь файл, но можно выйти, как только найдет совпадение.
Микель
3
Что мне делать, если я не хочу печатать строку с шаблоном соответствия?
Tommy.carstensen
34

распечатать до и включая матч:

awk '{print} /pattern/ {exit}' filename
sed '/pattern/q' filename

распечатать до НО НЕ включая матч:

awk '/pattern/ {exit} {print}' filename
sed '/pattern/Q' filename
Гленн Джекман
источник
11
Qэто круто, но гну конкретный afaik, sed -n '/pattern/!p;//q'будет более портативным.
don_crissti
@don_crissti: вы должны сделать его ответ, я думаю , что это хороший один (: Я немного любопытно , как это работает, хотя я считаю. !делает pприменить к линиям не совпадающих pattern, но потом //qменя смущает ...
JWD
2
@don_crissti: ах, я понял - //означает «предыдущее регулярное выражение» (я думал, что это означает «соответствовать пустой строке»). Я думаю , что более короткий вариант того же раствора: sed -n '/pattern/q;p?
JWD
@ JWD - действительно, это короче. 👍
don_crissti
1

Следующие grepметоды чистого GNU не эффективны.

Поиск всего до первого экземпляра строки " foo " в файловой строке , используя три grepс:

grep -m 1 -B $(grep -n -m 1 foo bar | grep -o '^[0-9]*') foo bar

Соответствует до последнего экземпляра " foo ":

grep -oPz "(?s)[^\n]*${s}.*?\n.*?foo.*?\n" bar

Примечание: подробности о последнем grepможно найти в: Regex (grep) для многострочного поиска .

АРУ
источник
Зачем когда-либо хотеть использовать 7 grepс (+ pcre), когда нужно просто запустить один sedвызов: sed 'x;/./G;//!x;/foo/p;//s/.*//;x;d'??
don_crissti
@don_crissti, ваш sedкод, кажется, стоит своего ответа, или может быть добавлен к одному из других. Re 7 greps: Потому что не было grepответа ... (плюс, ответ помогает показать, почему нет.)
agc
Это не мой код, просто нажмите на него ... Даже если это был мой код, он не отвечает на вопрос здесь, поэтому я бы не опубликовал его как ответ.
don_crissti
1

Добавляя к ответу Микеля выше ...


Для того, чтобы напечатать все строки до, но не включая , первую строчку в FILEсодержащем PATTERN, попробуйте:

  • sed '/.*PATTERN.*/{s///;q;}' FILE

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


Пост-скрипт:

Самый простой / понятный способ предотвратить вывод дополнительной строки в конце (без использования другого инструмента) - это снова запустить sed и удалить новую заключительную строку:

sed '/.*PATTERN.*/{s///;q;}' FILE | sed '$d'

... и так как мы сейчас удаляем эту строку, наша предыдущая работа избыточна, и мы можем упростить до:

sed '/PATTERN/q' FILE | sed '$d'
Джим гришем
источник
Ответ Гленна - и мой комментарий там - показывают, как это сделать с помощью одного sedвызова.
don_crissti
(Спасибо за это - я видел ваш комментарий к ответу agc, но либо пропустил другой, либо просто просмотрел его, потому что мой мозг не любит двойных негативов.) Поскольку я использовал это как в a, так tcshи в bashпсевдониме, мне нужно было убедиться, что я имел относительно сжатое однострочное решение, которое работало как в стандарте, так и в GNU sed(для переносимости); все требования, которые ваш вклад вполне может удовлетворить. Как человек, который использует sed очень редко, моим самым важным требованием было то, что я мог быстро понять, когда я хочу легко изменить или изменить его назначение через годы.
Джим Гришам
1

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

head -n $(grep -n pattern filename | cut -d: -f1) filename

Если эта команда предназначена для скрипта, я буду искать более элегантные (и, возможно, эффективные) решения. Если это одноразовая команда или сценарий выброса, мне все равно.

lesmana
источник
1
Хорошая идея, но три команды, когда одна будет делать.
Микель
1
Знание основ действительно очень хорошо. Однако лучше знать правильный инструмент для работы.
соулмердж
Если эта команда предназначена для скрипта, я буду искать более элегантные (и, возможно, эффективные) решения. Если это однократная команда (или сценарий одноразового использования), то мне все равно.
Lesmana
0

Вы также можете использовать один из следующих

tac ./test | grep -B $(cat ./test | wc -l) -m 1 'pattern'|tac 

или же

tac ./test |head -n $(tac ./test | grep -n 'pattern' | cut -d: -f1 | head -n 1)|tac

или же

tac ./test |sed ':a;N;$!ba;s/\n/'"pattern"'/g' | sed 's/'"patternpattern"'/\n/g'|head -n 1|sed 's/'"pattern"'/\n/g'|tac

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

Второй вариант ищет номер строки первого совпадения (вы также можете изменить это, изменив внутреннюю «голову»), а затем использует заголовок для этого номера.

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

pattern
texttexttext
texttexttext texttexttext
texttexttexttexttexttexttexttexttext
pattern
texttexttext
pattern 
texttexttext
texttexttexttexttexttexttexttexttext

и так далее

user122778
источник
2
подумайте, как объяснить, как они работают, особенно потому, что эта sedкоманда внизу довольно грубая.
Strugee
первая опция очень похожа на предложенную OP, только она обеспечивает отображение достаточного количества киней перед контекстом путем подсчета строк в filr,
user122778