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


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.