Показати весь файл до відповідності


71

grep --before-context 5 показує 5 рядків перед матчем.

Хочу показати все перед матчем.
Робити це grep --before-context 99999999буде добре, але це не дуже ... професійно.

Як показати весь файл до відповідності?

Відповіді:


95

Sed краще для цього.

Просто зробіть:

sed '/PATTERN/q' FILE

Це працює так:

Для кожного рядка ми дивимось, чи відповідає /PATTERN:

  • якщо так, ми роздруковуємо його та закриваємо
  • інакше ми друкуємо його

Це найефективніше рішення, адже як тільки воно бачить PATTERN, воно припиняється. Без цього qsed продовжував би читати решту файлів і нічого не робити з цим. Для великих файлів це може змінити значення.

Цей трюк також можна використовувати для наслідування head:

sed 10q FILE

Щойно спробував, він лише виводить перший рядок файлу ... навіть якщо збіг знаходиться у рядку 38.
— Ніколас Рауль,

Добре працює для мене. Чи можете ви навести приклад фактичного вводу та виводу? І команда, яку ви виконуєте як є.
— Мікель

Я спробував вашу команду, перш ніж ви її відредагували, це було: sed '/ PATTERN / p; q' ФАЙЛ
— Ніколас Рауль

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

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Класний, але специфічний афаік, 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 " на панелі файлів , використовуючи три greps:

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 greps (+ pcre), коли справа в тому, щоб просто запустити одне sedвиклик: sed 'x;/./G;//!x;/foo/p;//s/.*//;x;d'??
— don_crissti

@don_crissti, ваш sedкод здається вартим власної відповіді, або його можна додати до одного з інших. Re 7 greps: Тому що grepвідповіді не було ... (плюс, відповідь допомагає показати, чому ні.)
— agc

Це не мій код, просто натисніть на нього ... Навіть якщо це був мій код, він не відповідає тут Q, тому я не ставлю його як відповідь.
— 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, але або пропустив інший, або просто прокинув його, тому що мій мозок не любить подвійних негативів.) Оскільки я використовував це як tcshу bashпсевдонімі, так і потрібно було переконатися, що я мав відносно стисле однолінійне рішення, яке працювало як у стандартному, так і в GNU sed(для мобільності); всі вимоги, які ваш внесок, можливо, дуже добре виконали. Як хтось, хто sed дуже рідко використовує , найголовнішою моєю вимогою було те, що я міг швидко зрозуміти, коли хочу легко редагувати чи повторно призначити це через роки.
— Джим Гришам

1

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

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

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


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

Перший варіант дуже схожий на те, що запропонував ОП, тільки він гарантує, що ти покажеш достатньо рядків перед контекстом, підрахувавши рядки у файлі

Другий варіант шукає номер рядка першого збігу (ви також можете змінити це, змінивши внутрішню "голову"), а потім використовує заголовок цього номера

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

pattern
texttexttext
texttexttext texttexttext
texttexttexttexttexttexttexttexttext
pattern
texttexttext
pattern 
texttexttext
texttexttexttexttexttexttexttexttext

і так далі


2
Подумайте, як пояснити, як це працює, тим більше, що ця sedкоманда внизу є начебто загальною.
— strugee

перший варіант дуже схожий на те, що запропонував ОП, але він гарантує, що ти покажеш достатню кількість кінек перед контекстом, підраховуючи рядки у filr,
— користувач122778
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.