2013-07-28 42 views
39

Chciałbym wydrukować pojedynczą linię bezpośrednio za linią zawierającą pasujący wzór. Moja wersja sed nie będzie mieć następującą składnię (IT bomby na +1p.), Który wydaje się proste rozwiązanie:Drukowanie za pomocą polecenia sed lub awk linii zgodnej z pasującym wzorcem

sed -n '/ABC/,+1p' infile 

Zakładam awk lepiej byłoby zrobić multilinii przetwarzania, ale nie jestem pewien, jak aby to zrobić.

+1

http://sed.sourceforge.net/sed1line.txt – devnull

Odpowiedz

27

Jest to linia po, która pasuje do tego, co Cię interesuje, prawda? W sed, które mogą być realizowane w taki sposób:

sed -n '/ABC/{n;p}' infile 

Alternatywnie Opcja grepa może być to, czego szukasz.

-A NUM, Print NUM lines of trailing context after matching lines. 

Na przykład, biorąc pod uwagę następujący plik wejściowy:

foo 
bar 
baz 
bash 
bongo 

Można użyć następujących:

$ grep -A 1 "bar" file 
bar 
baz 
$ sed -n '/bar/{n;p}' file 
baz 

nadzieję, że pomoże.

+0

Uwaga: '{n; p}' wydaje się być obsługiwane przez GNU sed, ale nie BSD sed. (Dziękuję za chrzaniasty głos odpowiedzi, mam wielki szacunek dla awk i użyłem go, ale staram się unikać ponownego jego barokowego języka, kiedy tylko jest to możliwe. (Kiedy potrzebuję awk używam perla).) – Mars

+4

Korekta: Miałem sukces z BSD sed, dodając ';': 'sed -n/bar/{n; p;}'. Działa również z GNU sed. – Mars

+0

Przy _original_ sed musiałbyś napisać 'sed -n '/ bar/{; n; p;}'' ponieważ '{' i '}' zostały sparsowane dokładnie tak samo jak polecenia listowe. – zwol

2

awk Wersja:

awk '/regexp/ { getline; print $0; }' filetosearch 
+1

Dzięki! Zapomniałem o opcji -A w grep; działa idealnie z parametrem +1 (linia z dopasowanym wzorem nie jest drukowana). – user1537723

+1

Spowoduje to niepowodzenie w tajemniczy sposób, kiedy najmniej się tego spodziewamy i będzie trudne do ulepszenia w przyszłości. Upewnij się, że jesteś gotowy iw pełni zrozumieć http://awk.info/?tip/getline przed podjęciem decyzji o użyciu 'getline'. –

+0

Spraw, aby http://awk.freeshell.org/AllAboutGetline –

98

Nigdy nie używać słowa "wzór", jak to jest bardzo niejednoznaczny. Zawsze używaj "string" lub "regexp" (lub w powłoce "wzór globowania"), w zależności od tego, co naprawdę masz na myśli.

Konkretna odpowiedź chcesz to:

awk 'f{print;f=0} /regexp/{f=1}' file 

lub specjalizujący się w bardziej ogólne rozwiązanie n-tego rekordu po regexp (idiom "C" poniżej):

awk 'c&&!--c; /regexp/{c=1}' file 

następujące idiomy opisać jak wybrać zakres rekordów z określonym dopasowaniem do wyrażeń regularnych:

a) Wydrukuj wszystkie zapisy z niektórych wyrażeń:

awk '/regexp/{f=1}f' file 

b) Drukuj wszystkie zapisy po pewnym regexp:

awk 'f;/regexp/{f=1}' file 

c) Print n-ty rekord po pewnym regexp:

awk 'c&&!--c;/regexp/{c=N}' file 

d) print każdy rekord z wyjątkiem N-tego rekordu po pewnym regexp:

awk 'c&&!--c{next}/regexp/{c=N}1' file 

e) Wydrukuj N rekordów po krótkim referendum:

awk 'c&&c--;/regexp/{c=N}' file 

f) Drukuj każdy rekord z wyjątkiem zapisów N po jakimś regexp:

awk 'c&&c--{next}/regexp/{c=N}1' file 

g) wydrukować rekordy N od jakiegoś regexp:

awk '/regexp/{c=N}c&&c--' file 

Zmieniłem nazwę zmiennej z "f" dla "znalezionego" na "c" dla "count", gdzie jest bardziej wyraziste, czym właściwie jest zmienna.

+2

awesome @EdMorton –

+2

możesz wyjaśnić, co robi '' c &&! - c''? Dzięki! – zack

+1

Jeśli c jest niezerowe, zmniejsz go i sprawdź, czy jest teraz zero. Jeśli więc c zaczyna się od pewnej liczby dodatniej, powiązana akcja zostanie wykonana po odliczeniu od tej liczby do zera. Część "jeśli c jest niezerowa" to strażnik, który upewnia się, że c nie idzie dalej w liczby ujemne i potencjalnie zawija się do pozytywu, jeśli plik wejściowy jest masywny. –

0

Jeśli wzór pasuje, skopiuj następną linię do bufora wzoru, usuń powrót, a następnie zakończ - efektem strony jest wydrukowanie.

sed '/pattern/ { N; s/.*\n//; q }; d' 
+1

'q' nie jest wcale rozszerzeniem GNU. Jest to standardowe polecenie 'sed'. – tripleee

+0

Yup * tripleee "... UR prawy ... –

3

musiałem wydrukować wszystkie linie na wzór (ok Ed, REGEX), więc zdecydowałem się na ten jeden:

sed -n '/pattern/,$p' # prints all lines after (and including) the pattern 

Ale ponieważ chciałem, aby wydrukować wszystkie linie po (i nie wzór)

sed -n '/pattern/,$p' | tail -n+2 # all lines after first occurrence of pattern 

Przypuszczam, w twoim przypadku można dodać head -1 na koniec

sed -n '/pattern/,$p' | tail -n+2 | head -1 # prints line after pattern 
+0

Istnieje drugi sposób zabezpieczenia twojego drugiego przypadku:' sed '0,/regex/d'' – tlwhitec

1

Właściwie sed -n '/pattern/{n;p}' filename zawiedzie jeśli pattern mecz continuous linie:

$ seq 15 |sed -n '/1/{n;p}' 
2 
11 
13 
15 

oczekiwanych odpowiedzi powinny być:

2 
11 
12 
13 
14 
15 

Moje rozwiązanie to:

$ sed -n -r 'x;/_/{x;p;x};x;/pattern/!s/.*//;/pattern/s/.*/_/;h' filename 

Na przykład:

$ seq 15 |sed -n -r 'x;/_/{x;p;x};x;/1/!s/.*//;/1/s/.*/_/;h' 
2 
11 
12 
13 
14 
15 

wyjaśnia:

  1. x;: na początku każdej linii od wejścia, użyj polecenia x wymieniać zawartość w pattern space & hold space.
  2. /_/{x;p;x};: jeśli pattern space, który jest hold space rzeczywiście zawiera _ (to tylko indicator wskazaniem, czy ostatnia linia dopasowana do pattern lub nie), a następnie użyć x wymieniać rzeczywistej zawartości current line do pattern space, korzystania p do druku current line i x, aby odzyskać tę operację.
  3. x: odzyskaj zawartość w pattern space i hold space.
  4. /pattern/!s/.*//: jeśli current line NIE pasuje pattern, co oznacza, że ​​nie należy wydrukować następującym po linii, a następnie użyć polecenia s/.*// usunąć całą zawartość w pattern space.
  5. /pattern/s/.*/_/: jeśli current line mecze pattern, co oznacza, że ​​należy wydrukować następujące linii obok, to musimy ustawić indicator powiedzieć sed drukowanie następnej linii, więc używaj s/.*/_/ zastąpić całą zawartość w pattern space do _ (drugi Polecenie użyje go do oceny, czy ostatni wiersz pasował do pattern czy nie).
  6. h: zastąpić hold space zawartością w pattern space; następnie treść w hold space jest ^_$, co oznacza, że ​​current line pasuje do pattern lub ^$, co oznacza, że ​​NIE jest zgodny z .
  7. Piąty i szósty krok NIE mogą się zmienić, ponieważ po s/.*/_/, pattern space NIE może się równać /pattern/, więc s/.*// MUSI zostać wykonany!
+0

Dziękuję za miłe wyjaśnienie, ++ ve – RavinderSingh13

0

To może pracować dla Ciebie (GNU sed):

sed -n ':a;/regexp/{n;h;p;x;ba}' file 

Zastosowanie SEDS opcja grep podobny -n a jeśli bieżący wiersz zawiera wymagane regexp zastąpić bieżącą linię z kolejnym, skopiuj ten wiersz do miejsca przechowywania (HS), wydrukuj linię, zamień przestrzeń wzoru (PS) na HS i powtórz.