Usuń wiersz zawierający określony ciąg i następujący wiersz

70

używam tego

cat foo.txt | sed '/bar/d'

aby usunąć wiersze zawierające ciąg znaków barw pliku.

Chciałbym jednak usunąć te linie i linię bezpośrednio po niej . Korzystnie sed, awklub inne narzędzie, które jest dostępne w mingw32.

Jest to rodzaj odwrocie, co mogę dostać grepsię -Ai -Bzanim do drukowania pasujących wierszy, jak również linie / po dopasowanej linii.

Czy jest jakiś prosty sposób na osiągnięcie tego?

jakub.g
źródło
2
Tylko dla informacji: analizuję logi, w których wpisy są dwuliniowe. Więc chcę znaleźć wpis pasujący do wzoru i usunąć go, a także następny wiersz. Dlatego nie muszę obsługiwać kolejnych linii meczowych, ale i tak dziękuję za kompletność twoich odpowiedzi!
jakub.g

Odpowiedzi:

74

Jeśli masz GNU sed (tak niewbudowany Linux lub Cygwin):

sed '/bar/,+1 d'

Jeśli masz bardwa kolejne wiersze, spowoduje to usunięcie drugiego wiersza bez jego analizy. Na przykład, jeśli masz plik 3-liniowy bar/ bar/ foo, foolinia pozostanie.

Gilles
źródło
1
+1 za długość :) W moim konkretnym przykładzie nie mam kolejnych bars, więc ten jest bardzo łatwy do zapamiętania.
jakub.g
11
sed '/bar/d'jeśli chcesz po prostu „Usuń wiersz zawierający określony ciąg”, a nie następny.
AJP
Jeśli chcę usunąć wszystkie linie po matematyce?
Pandya,
1
@Pandya To jest inne. Możesz użyć np.sed '/math/q'
Gilles
1
@AK Jeśli chcesz tylko usunąć pasującą linię, jest to jeszcze prostsze:sed '/bar/d'
Gilles
16

Jeśli barmoże wystąpić na kolejnych liniach, możesz:

awk '/bar/{n=2}; n {n--; next}; 1' < infile > outfile

które można dostosować, aby usunąć więcej niż 2 linie, zmieniając 2 powyżej z liczbą linii do usunięcia, w tym pasującą.

Jeśli nie, łatwo to zrobić sedza pomocą rozwiązania @MichaelRollins lub:

sed '/bar/,/^/d' < infile > outfile
Stéphane Chazelas
źródło
Drugim plusem w roztworze AWK jest to, że mogę wymienić /bar/z /bar|baz|whatever/. W sedtej składni wydaje się nie działać.
jakub.g
@ jakub.g, mam GNU sed (teraz v4.4). Nie jestem pewien co do innych. Wiem, że domyślnie używa „podstawowej” składni wyrażeń regularnych, dlatego twój przykład nie działał. Aby osiągnąć to, co chcesz, możesz albo wstawić odwrotny ukośnik przed każdą pionową linią, albo możesz poprosić sedo użycie „rozszerzonych” wyrażeń regularnych. Więcej informacji tutaj: gnu.org/software/sed/manual/html_node/… . Należy pamiętać, że dotyczy to greprównież. Oto mój własny przykład praca: echo $'0a\n1b\n2c' | sed '/0a\|1b/d'.
Victor Yarema
12

Nie jestem biegły w sed, ale łatwo to zrobić w awk:

awk '/bar/{getline;next} 1' foo.txt 

Skrypt awk brzmi: dla linii zawierającej pasek, pobierz następną linię (getline), a następnie pomiń wszystkie dalsze przetwarzanie (następne). Wzór 1 na końcu drukuje pozostałe linie.

Aktualizacja

Jak wskazano w komentarzu, powyższe rozwiązanie nie działało z kolejnymi bar. Oto poprawione rozwiązanie, które uwzględnia to:

awk '/bar/ {while (/bar/ && getline>0) ; next} 1' foo.txt 

Teraz czytamy, aby pominąć wszystkie linie / bar /.

Hai Vu
źródło
1
Aby powielić grep -A100%, musisz również barpoprawnie obsłużyć dowolną liczbę kolejnych linii (usuwając cały blok i 1 linię po).
jw013,
7

Będziesz chciał skorzystać z możliwości skryptów sed, aby to osiągnąć.

$ sed -e '/bar/ { 
 $!N
 d
 }' sample1.txt

Przykładowe dane:

$ cat sample1.txt 
foo
bar
biz
baz
buz

Polecenie „N” dołącza następny wiersz danych wejściowych do obszaru wzorów. W połączeniu z linią z dopasowania wzorca (/ bar /) będą to linie, które chcesz usunąć. Następnie możesz normalnie usunąć polecenie „d”.

Michael Rollins
źródło
Jak wpisać nowy wiersz w konsoli? Czy to tylko skrypt?
jakub.g
@ jakub.g: with GNU sed:sed -e '/bar/{N;d}' sample1.txt
Cyrus
2

Jeśli jakikolwiek wiersz bezpośrednio po dopasowaniu zostanie usunięty, wówczas twój sedprogram będzie musiał rozważyć kolejne dopasowania. Innymi słowy, jeśli usuniesz linię po dopasowaniu, które również pasuje, prawdopodobnie prawdopodobnie powinieneś również usunąć linię po tym.

Jest zaimplementowany po prostu - ale trzeba trochę popatrzeć.

printf %s\\n     0 match 2 match match \
                 5 6 match match match \
                 10 11 12 match 14 15  |
sed -ne'x;/match/!{g;//!p;}'

0
6
11
12
15

Działa poprzez zamianę przestrzeni wstrzymania i wzorów dla każdej wczytywanej linii - dzięki czemu za każdym razem można porównać ostatnią linię z bieżącą. Tak więc, gdy sedczyta linię, wymienia zawartość swoich buforów - a poprzednia linia jest wówczas zawartością bufora edycji, podczas gdy bieżąca linia jest umieszczana w miejscu wstrzymania.

sedSprawdza więc poprzedni wiersz pod kątem dopasowania do match, a jeśli go !nie znaleziono, uruchamiane są dwa wyrażenia w {funkcji }. sedwola gi miejsca przechowywania przez nadpisanie przestrzeń Pattern - co oznacza, że bieżąca linia jest wówczas w obu luku i wzór przestrzeni - i wtedy będzie //to sprawdzić na mecz do jej ostatnio skompilowanego wyrażenia regularnego - match- a jeśli to nie matchona jest pzabarwiony

Oznacza to, że linia jest drukowana tylko wtedy, gdy nie, a linia poprzednio poprzednia nie . Zapobiega także wszelkim niepotrzebnym zamianom sekwencji es.match matchmatch

Jeśli chcesz wersję, która może upuszczać dowolną liczbę wierszy pojawiających się po matchniej, wymaga trochę więcej pracy:

printf %s\\n    1 2 3 4 match  \
                match match 8  \
                9 10 11 12 13  \
                14 match match \
                17 18 19 20 21 |
sed -net -e'/match/{h;n;//h;//!H;G;s/\n/&/5;D;}' -ep

... zamień 5 na liczbę linii (w tym dopasowaną linię) , którą chcesz usunąć ...


1
2
3
4
12
13
14
21
mikeserv
źródło