Знайдіть усі події у файлі з 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.