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

Używam tego

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

aby usunąć w pliku wiersze zawierające ciąg bar.

Chciałbym jednak usunąć te wiersze i bezpośrednio po nim . Najlepiej w sed, awk lub innym narzędziu dostępnym w MinGW32.

To coś w rodzaju odwrotności tego, co mogę uzyskać w grep z -A i -B, aby również wydrukować pasujące wiersze jako linie przed / po dopasowanej linii.

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

Komentarze

  • Tylko dla informacje: I ' m analizuję dzienniki, w których wpisy są dwuwierszowe. Chcę więc znaleźć wpis pasujący do wzorca i usunąć go, jak również następny wiersz. Dlatego nie ' nie muszę zajmować się kolejnymi liniami dopasowania, ale i tak dziękuję za kompletność odpowiedzi!

Odpowiedź

Jeśli masz GNU sed (a więc nie osadzony Linux lub Cygwin):

sed "/bar/,+1 d" 

Jeśli mają bar w dwóch kolejnych wierszach, spowoduje to usunięcie drugiego wiersza bez jego analizy. Na przykład, jeśli masz plik z 3 wierszami bar / bar / foo, wiersz foo pozostanie.

Komentarze

  • +1 za długość 🙂 W szczególności przykład Nie ' nie mam następujących po sobie bar, więc ten jest bardzo łatwy do zapamiętania.
  • sed '/bar/d' jeśli chcesz tylko ” usunąć wiersz zawierający określony ciąg ” i nie następny.
  • Jeśli chcę usunąć wszystkie wiersze po matematyce, to?
  • @Pandya To ' jest inne. Możesz użyć np. sed '/math/q'
  • @ A.K. Jeśli chcesz tylko usunąć pasującą linię, ' jest jeszcze prostsze: sed '/bar/d'

Odpowiedź

Jeśli bar może pojawić się w kolejnych wierszach, możesz:

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

, który można dostosować do usuwania więcej niż 2 wierszy, zmieniając 2 powyższe z liczbą wierszy do usunięcia, w tym pasującą.

Jeśli nie, to „łatwo to zrobić w sed z rozwiązaniem @MichaelRollins” lub:

sed "/bar/,/^/d" < infile > outfile 

Komentarze

  • Kolejną zaletą rozwiązania AWK jest to, że mogę zamienić /bar/ na /bar|baz|whatever/. W sed ta składnia nie ' wydaje się działać.
  • @ jakub.g, mam sed GNU ( v4.4 teraz). Nie jestem pewien co do innych. Wiem, że domyślnie używa ” podstawowej ” składni wyrażeń regularnych, dlatego w Twoim przykładzie nie ' t działa. Aby osiągnąć to, co chcesz, możesz umieścić ukośnik odwrotny przed każdą pionową linią lub możesz poprosić sed o użycie ” rozszerzonego ” wyrażeń regularnych. Więcej informacji tutaj: gnu.org/software/sed/manual/html_node/… . Pamiętaj, że dotyczy to również grep. Oto ' mój własny przykład roboczy: echo $'0a\n1b\n2c' | sed '/0a\|1b/d'.

Odpowiedź

Nie znam biegle seda, ale łatwo jest to zrobić w awk:

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

Skrypt awk czyta: dla linii zawierającej bar, pobierz następną linię (getline), a następnie pomiń wszystkie dalsze przetwarzanie (next). Wzorzec 1 na końcu wyświetla pozostałe wiersze.

Aktualizacja

Jak wskazano w komentarzu, powyższe rozwiązanie nie zadziałało z kolejnymi bar. Oto poprawione rozwiązanie, które bierze je pod uwagę:

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

Kontynuujemy czytanie, aby pominąć wszystkie / bar / lines.

Komentarze

  • Aby replikować grep -A w 100%, musisz również obsłużyć dowolną liczbę kolejnych bar wiersze poprawnie (usuwając cały blok i 1 wiersz po nim).

Odpowiedz

Aby to osiągnąć, będziesz chciał skorzystać z możliwości skryptów seda.

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

Przykładowe dane:

$ cat sample1.txt foo bar biz baz buz 

Polecenie „N” dodaje następny wiersz danych wejściowych do przestrzeni wzorców. To w połączeniu z linią z dopasowania wzorca (/ bar /) będzie liniami, które chcesz usunąć. Możesz wtedy usuń normalnie za pomocą polecenia „d”.

Komentarze

  • Jak wpisać nową linię w konsoli? Czy to jest tylko skrypt?
  • @ jakub.g: z GNU sed: sed -e '/bar/{N;d}' sample1.txt

Odpowiedź

Jeśli jakikolwiek wiersz występujący bezpośrednio po dopasowaniu powinien zostać usunięty, wówczas program sed będzie musiał uwzględnić kolejne dopasowania. Innymi słowy, jeśli usuniesz linię po dopasowaniu, która również pasuje, prawdopodobnie powinieneś usunąć również linię następującą po tym.

Jest to dość proste – ale musisz trochę spojrzeć w tył .

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ę spacji wstrzymania i wzorca dla każdej wczytywanej linii – więc za każdym razem ostatnią linię można porównać z bieżącą. Więc kiedy sed czyta linię, wymienia zawartość swoich buforów – a poprzednia linia jest wtedy zawartością jej bufora edycyjnego, podczas gdy bieżąca linia jest umieszczana w przestrzeni wstrzymania.

Zatem sed sprawdza poprzednią linię pod kątem dopasowania do match , a jeśli jego ! nie znaleziono dwóch wyrażeń w funkcji { } zostały uruchomione. sed g zwalnia przestrzeń wstrzymania, zastępując przestrzeń wzoru – co oznacza, że bieżąca linia znajduje się wtedy zarówno w przestrzeni wstrzymania, jak i wzoru – a następnie // sprawdzi dopasowanie do ostatnio skompilowanego wyrażenia regularnego – match – i jeśli to nie jest match , to jest p rintowane.

Oznacza to, że wiersz jest drukowany tylko wtedy, gdy nie jest match , a bezpośrednio poprzedni wiersz nie match . Eliminuje również wszelkie niepotrzebne zamiany sekwencji match es.

Jeśli potrzebujesz wersji, która może gubić dowolną liczbę wierszy występujące po match wymagałoby 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ę wierszy (łącznie z dopasowaną linią) , które chcesz usunąć …


1 2 3 4 12 13 14 21 

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *