Знайдіть усі події у файлі з sed


15

Використання ОС OPEN STEP 4.2 ... На даний момент я використовую таку sedкоманду:

sed -n '1,/141.299.99.1/p' TESTFILE | tail -3

Ця команда знайде один екземпляр у файлі з ip 141.299.99.1, а також перед ним включить 3 рядки, що все добре, за винятком того, що я також хотів би знайти всі екземпляри IP та 3 рядки перед ним і не тільки перший.


1
Будь ласка, завжди включайте вашу ОС. Рішення дуже часто залежать від операційної системи, яка використовується. Використовуєте Unix, Linux, BSD, OSX, щось інше? Яка версія?
terdon

ВЕЛИКИЙ ОК! Використання Open Step версії 4.2 досить старе, і включені оболонки не містять багатьох функцій, згаданих у відповідях нижче.
Дейл

З цікавості - що таке система OPEN STEP 4.2 і для чого вона використовується сьогодні?
Thorbjørn Ravn Andersen

(і якщо Perl є в наявності, ви можете дійсно зробити багато приємних справ саме з цим)
Thorbjørn Ravn Andersen

@ ThorbjørnRavnAndersen Можливо, це так: en.wikipedia.org/wiki/OpenStep
Barmar

Відповіді:


4

Ось спроба емуляції grep -B3за допомогою рухомого вікна sed на основі цього прикладу GNU sed (але, сподіваємось, сумісний з POSIX - з підтвердженням до @ StéphaneChazelas):

sed -e '1h;2,4{;H;g;}' -e '1,3d' -e '/141\.299\.99\.1/P' -e '$!N;D' file

Перші два вирази складають буфер багаторядкового шаблону і дозволяють йому обробляти крайовий регістр, в якому до першого матчу є менше 3 рядків попереднього контексту. Середній вираз (збіг з регулярними виразками) друкує рядок у верхній частині вікна, поки потрібний текст збігу не прошивається через буфер шаблону. Остаточне $!N;Dпрокручування вікна на один рядок, за винятком випадків, коли воно доходить до кінця введення.


-eне є специфічним для GNU Щоб бути POSIX / портативним, він вам потрібен, оскільки після цього нічого не може бути }(і вам потрібно ;перед цим).
Stéphane Chazelas

Спасибі @ StéphaneChazelas - так ти кажеш, що для того, щоб бути POSIX / переносним, першу групу потрібно розділити / змінити як -e '1h;2,4{H;g;}' -e '1,3d'? У мене немає системи, яка не є GNU для тестування (а --posixперемикач sed GNU, здається, не хвилює).
steeldriver

1
Так, в Linux ви можете протестувати іншу реалізацію за допомогою інструментального sedінструменту heirloom, який є нащадком традиційного Unix sed. Специфікація POSIX / Unix sedзнаходиться за адресою pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html
Stéphane Chazelas

Я отримую подію, яку не знайдено в жодному з цих: N; D ': подія не знайдена. Я десь пропускаю синтаксис? Спасибі!!
Дейл

Вибачте, що щойно я зрозумів, що моя остання редакція після виразу першого -е пропустила єдину цитату. Я виправив це зараз - чи можете ви спробувати ще раз із вищевказаним виразом?
steeldriver

10

grep зробимо кращу роботу з цього:

grep -B 3 141.299.99.1 TESTFILE

В -B 3кошти для друку три рядки перед кожним матчем. Це буде надруковано --між кожною групою рядків. Щоб відключити це, використовуйте --no-group-separatorтакож.

Ця -Bопція підтримується також GNUgrep і більшості версій BSD ( OSX , FreeBSD , OpenBSD , NetBSD ), але технічно це не стандартний варіант.


1
Майкл Гомер - Дякую. У мене немає варіанту - B. Будь-які інші ідеї?
Дейл

@Dale Чи можете ви встановити GNU grep? Це дасть вам можливість.
Barmar

9

З допомогою sedвас можна зробити розсувне вікно.

sed '1N;$!N;/141.299.99.1/P;D'

Це робить це. Але будьте обережні - bashбожевільна поведінка розширюється ! навіть при цитуванні !!! в командний рядок з вашої історії команд може злегка зійти з розуму. Префікс команди, set +H;якщо ви виявите, що це так. Щоб потім повторно ввімкнути це (але чому ???), зробіть set -Hпотім.

Це, звичайно, буде застосовуватися тільки тоді , коли ви були з допомогою bash- хоча я не вірю , що ти. Я абсолютно впевнений , що ви працюєте з csh- (що трапляється шкаралупа , чия поведінка божевільного bashемулює з розширенням історії, але , можливо , не до крайнощів З оболонкою взяла її) . Так , ймовірно\! , повинен працювати. Я сподіваюсь.

Це все переносний код: POSIX так описує свої три оператори: (хоча варто зазначити, що я лише підтвердив, що цей опис існував ще в 2001 році)

[2addr]N Додайте наступний рядок введення, за вирахуванням його закінчувальної \nлінії виходу, до простору шаблону, використовуючи вбудовану \nлінію ewline для відокремлення доданого матеріалу від вихідного матеріалу. Зауважте, що номер поточного рядка змінюється.

[2addr]P Напишіть простір шаблону, до першого рівня \nлінії, до стандартного виводу.

[2addr]D Видаліть початковий сегмент простору шаблону через першу \nлінію ewline та розпочніть наступний цикл.

Отже, на першому рядку ви додаєте додатковий рядок до простору візерунка, так що це виглядає приблизно так:

^line 1s contents\nline 2s contents$

Потім на першому рядку та в кожному наступному рядку - крім самого останнього - ви додаєте ще один рядок до простору малюнка. Так це виглядає приблизно так:

^line 1\nline 2\nline 3$

Якщо ваша ip-адреса знайдена в межах вас, Pперейдіть до першого нового рядка, тож просто рядок 1 тут. В кінці кожного циклу ви Dвибираєте те саме і починаєте все з того, що залишається. Отже, наступний цикл виглядає так:

^line 2\nline 3\nline 4$

...і так далі. Якщо ваш ip знайдеться на будь-якому з цих трьох, найстаріший буде друкуватись - кожного разу. Отже, у вас завжди лише три лінії попереду.

Ось короткий приклад. Я отримаю друкований буфер з трьох рядків для кожного числа, що закінчується нулем:

seq 10 52 | sed '1N;$!N;/0\(\n\|$\)/P;D'

10
18
19
20
28
29
30
38
39
40
48
49
50

Це трохи складніше, ніж ваш випадок, тому що мені довелося чергувати або з 0\nнового рядка, або з 0$кінця простору шаблону, щоб більше нагадувати вашу проблему - але вони тонко відрізняються тим, що для цього потрібен якір - що може бути трохи важко зробити, оскільки шаблон-простір постійно зміщується.

Я використовував непарні випадки 10 і 52, щоб показати, що поки якір є гнучким, то такий і вихід. Повністю портативно, я можу досягти тих же результатів, замість того, щоб розраховувати алгоритм і робити:

seq 10 52 | sed '1N;$!N;/[90]\n/P;D'

І розширити пошук, обмеживши вікно - від 0 до 9 і 0 і від 3 рядків до двох.

У будь-якому випадку ви отримуєте ідею.


Дякую за всю Вашу наполегливу працю. Вибачте, де я б розмістив ім'я файлу, який би хотів його шукати?
Дейл

@Dale - моя погана. sed '...' $filename. До речі - я вийшов у періоди з вашої власної пошукової рядка, але це насправді не періоди в шаблоні - вони являють собою будь-який окремий символ. Ви повинні, мабуть, зробити, oct\.oct\.oct\.octщоб уникнути їх, щоб вони відповідали лише періодам.
mikeserv

Я намагався котитися з нею та різними <> символами, і мені не вдалося знайти події, які я отримую з іншими рішеннями, тому мені цікаво, чи моя ОС не сумісна з цими рішеннями.
Дейл

тепер результати з -> N; /141.299.99.1/P; D ': подія не знайдена.
Дейл

@Dale - перегляньте оновлення. Це повинно вам допомогти.
mikeserv

4

Оскільки ви згадуєте, що у вас немає -Bможливості зробити це grep, ви можете використовувати Perl (наприклад), щоб зробити розсувне вікно з 4-х рядків:

perl -ne '
    push @window,$_;
    shift @window if @window > 4;
    print @window if /141\.299\.99\.1/
' your_file

Відповідь Рамеша робить подібне awk.


Я не впевнений, чи підтримує це моя версія Perl, але спробую. Дуже дякую, що знайшли час, щоб відповісти на моє запитання - дуже вдячна!
Дейл

@Dale Ви дуже раді. Сумніваюся, що цей код використовує будь-які передові функції Perl.
Джозеф Р.

4

Якщо вони доступні, ви можете використовувати pcregrep :

pcregrep -M '.*\n.*\n.*\n141.299.99.1' file

Перевірка, чи є у мене PCREGREP. Мені подобається компактність команди. Дуже вдячний за ваш час та зусилля. Дякую!!!
Дейл

4

Ви можете реалізувати той самий базовий підхід, що й інші відповіді, які не є чіткими, у самій оболонці (це передбачає відносно недавню оболонку, яка підтримує =~):

while IFS= read -r line; do 
    [[ $line =~ 141.299.99.1 ]] && printf "%s\n%s\n%s\n%s\n" $a $b $c $line;
    a=$b; b=$c; c=$line; 
done < file 

Крім того, ви можете прив'язувати весь файл до масиву:

perl -e '@F=<>; 
        for($i=0;$i<=$#F;$i++){
          print $F[$i-3],$F[$i-2],$F[$i-1],$F[$i] if $F[$i]=~/141.299.99.1/
        }' file 

Моя оболонка дуже стара - Стів Джобс Відкритий крок. Хоча чудова ідея і дякую за ваш час !!! Дейл
Дейл

@ Продати підхід Perl буде працювати практично де завгодно. Скажіть, будь ласка, вашу операційну систему (додайте її до свого запитання), щоб ми могли запропонувати речі, які будуть працювати для вас.
terdon

Якщо я скопіюю ваш Perl і покладу його в NotePad і поставте його в один рядок, він працює! Питання - якщо я хотів би сказати 10 рядків перед схемою відповідності, де я міняв би 3 на 10? Спасибі!
Дейл

Я бачу, що я можу додати ще рядків назад, додавши більше операцій $ F [$ iX]. Спасибі!
Дейл

4

Якщо ваша система не підтримує grepконтекст, ви можете спробувати ack-grep :

ack -B 3 141.299.99.1 file

ack - такий інструмент, як grep, оптимізований для програмістів.


Мені подобається компактність команди, але моя система не підтримує в пошуку сторінок man. Чудова ідея і дякую вам за ваш час !!! Дейл
Дейл

@Dale: Дивно! Яка ваша ОС? Якщо у вас є perl, ви можете використовувати ack.
cuonglm

2
awk '/141.299.99.1/{for(i=1;i<=x;)print a[i++];print} {for(i=1;i<x;i++)
     a[i]=a[i+1];a[x]=$0;}'  x=3 filename

У цьому awkрішенні використовується масив, який завжди буде містити 3 рядки перед поточним шаблоном. Отже, коли шаблон узгоджений, вміст масиву разом із поточним шаблоном друкується.

Тестування

-bash-3.2$ cat filename
10.0.0.1
10.0.0.2
10.0.0.3
10.0.0.4
141.299.99.1
10.0.0.5
10.0.0.6
10.0.0.7
10.0.0.8
10.0.0.9
10.0.0.10
141.299.99.1
10.0.0.11
10.0.0.12
10.0.0.13
10.0.0.14
10.0.0.15
10.0.0.16
141.299.99.1
10.0.0.17
10.0.0.18
10.0.0.19

Після того, як я виконую команду, вихід,

10.0.0.2
10.0.0.3
10.0.0.4
141.299.99.1
10.0.0.8
10.0.0.9
10.0.0.10
141.299.99.1
10.0.0.14
10.0.0.15
10.0.0.16
141.299.99.1

так докладно - велике дякую Я спробую. Дуже вдячний за ваш час !! Дейл
Дейл

У мене є тестовий файл, і ваше рішення працює! Проблема, проте, коли я запускаю його у своєму великому виробничому файлі, він повертається із Занадто довгим записовим номером, тому вихід не може працювати з командою. Моя оригінальна команда вгорі цієї сторінки працює, але знаходить лише один екземпляр. Я вдячний за твою допомогу. Чи можу я щось зробити зі своєю оригінальною командою, щоб змусити її знайти декілька екземплярів?
Дейл

1

У більшості з них /141.299.99.1/також відповідатиме (наприклад) 141a299q99+1чи 141029969951тому. в регулярному виразі може бути представлений будь-який символ.

Використання /141[.]299[.]99[.]1/безпечніше, і ви можете додати додатковий контекст на початку і в кінці всього регулярного виразу , щоб переконатися , що він не відповідає 3141., .12, .104і т.д.


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