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/
. Wsed
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ę kolejnychbar
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