sed lub awk: usuń n wierszy następujących po wzorcu

106

Jak mieszać wzorce i zakresy liczbowe w sedzie (lub jakimkolwiek podobnym narzędziu - na przykład awk)? To, co chcę zrobić, to dopasować określone wiersze w pliku i usunąć następnych n wierszy przed kontynuowaniem, a chcę to zrobić jako część potoku.

Martin DeMello
źródło

Odpowiedzi:

189

Spróbuję tego.

Aby usunąć 5 linii po wzorze (w tym linię ze wzorem):

sed -e '/pattern/,+5d' file.txt

Aby usunąć 5 linii po wzorze (wyłączając linię ze wzorem):

sed -e '/pattern/{n;N;N;N;N;d}' file.txt
dogbane
źródło
14
Zauważ, że +Nwzorzec jest rozszerzeniem GNU. Zmień pierwszy nna a Nw drugim przykładzie, aby zawierał linię ze wzorem.
Wstrzymano do odwołania.
2
jak usunąć wszystkie linie po dopasowaniu wzorca? Używam sed -e '/ <! - # content end -> </div> /, $ d' out.txt, ale wyświetla błąd: sed: -e wyrażenie # 1, char 24: dodatkowe znaki po polecenie Z góry dziękuję.
N mol
8
To, co się dzieje, jest podobne, ale w każdym przypadku nieco inne. W pierwszej recepturze /pattern/,+5definiuje zakres, który zaczyna się od wiersza zawierającego „wzorzec” ( /pattern/) i kończy 5 wierszy później ( +5). Ostatni znak dto polecenie uruchamiane w każdym wierszu w tym zakresie, czyli „usuń”. W drugiej recepturze, zamiast dopasować zakres, dopasowuje tylko wiersz zawierający wzorzec ( /pattern/), a następnie wykonuje serię poleceń {n;N;N;N;N;d}:, które w zasadzie drukuje następną linię ( n), a następnie odczytuje i ostatecznie odrzuca następne 4 wiersze ( N;N;N;N;d).
pimlottc
18
W systemach Mac / OS X należy dodać średnik przed nawiasem zamykającym:sed -e '/pattern/{n;N;N;N;N;d;}' file.txt
AvL
1
Dla kompletności: Aby usunąć wszystkie wiersze następujące po określonym wzorcu, something wykonaj :, sed -E '/^something$/,$d'gdzie -Ejest rozszerzonym wyrażeniem regularnym przenośności POSIX.
not2qubit
7

Bez rozszerzeń GNU (np. Na macOS):

Aby usunąć 5 linii po wzorze (w tym linię ze wzorem)

 sed -e '/pattern/{N;N;N;N;d;}'

Dodaj, -i ''aby edytować lokalnie.

thakis
źródło
6

Proste awkrozwiązania:

Załóżmy, że wyrażenie regularne używane do znajdowania pasujących wierszy jest przechowywane w zmiennej powłoki $regexi liczba wierszy do pominięcia $count.

Jeśli pasująca linia również powinna zostać pominięta ( $count + 1linie są pomijane):

... | awk -v regex="$regex" -v count="$count" \
  '$0 ~ regex { skip=count; next } --skip >= 0 { next } 1'

Jeśli pasująca linia nie powinna być pomijana ( $countlinie po dopasowaniu są pomijane):

... | awk -v regex="$regex" -v count="$count" \
  '$0 ~ regex { skip=count; print; next } --skip >= 0 { next } 1'

Wyjaśnienie:

  • -v regex="$regex" -v count="$count"definiuje awkzmienne w oparciu o zmienne powłoki o tej samej nazwie.
  • $0 ~ regex pasuje do linii zainteresowania
    • { skip=count; next }inicjalizuje licznik pominięć i przechodzi do następnej linii, skutecznie pomijając pasującą linię; w drugim rozwiązaniu printpoprzednie nextzapewnia, że ​​nie jest ono pomijane.
    • --skip >= 0 zmniejsza liczbę pominięć i podejmuje działanie, jeśli jest (nadal)> = 0, co oznacza, że ​​linia powinna zostać pominięta.
    • { next } przechodzi do następnej linii, skutecznie pomijając bieżącą linię
  • 1jest powszechnie używanym skrótem { print }; to znaczy, że bieżący wiersz jest po prostu drukowany
    • Do tego polecenia docierają tylko niepasujące i niepominięte wiersze.
    • Powód, który 1jest równoważny { print }, 1jest interpretowany jako wzorzec boolowski, który z definicji zawsze przyjmuje wartość true, co oznacza, że ​​skojarzona z nim akcja (blok) jest wykonywana bezwarunkowo. Ponieważ w tym przypadku nie ma skojarzonej akcji, awkdomyślnie drukowany jest wiersz.
mklement0
źródło
3

To może Ci pomóc:

cat <<! >pattern_number.txt
> 5 3
> 10 1
> 15 5
> !
sed 's|\(\S*\) \(\S*\)|/\1/,+\2{//!d}|' pattern_number.txt |
sed -f - <(seq 21)
1 
2
3
4
5
9
10
12
13
14
15
21
potong
źródło
10
Wow, to tajemnicze.
pimlottc
3
Sprytne (aczkolwiek specyficzne dla GNU-Sed) rozwiązanie, ale niewiele osób odniesie na nim korzyści, chyba że dodasz wyjaśnienie. pattern_number.txtjest plikiem z dwiema kolumnami, zawierającym wzorzec do dopasowania w pierwszej kolumnie, aw drugiej liczbie linii do pominięcia. Pierwsze sedpolecenie przekształca plik w sedskrypt, który przeprowadza odpowiednie dopasowywanie i pomijanie; ten skrypt jest dostarczany za pośrednictwem -fi stdin ( -) do drugiego sedpolecenia. Drugie sedpolecenie działa na przykładowym pliku wejściowym ad-hoc utworzonym z danych wyjściowych programu, seq 21aby zademonstrować, że działa.
mklement0
Ponadto rozwiązanie ma jedno zastrzeżenie: metoda, której używa, aby nie pomijać pierwszej linii (tej pasującej do wzorca), ma ten efekt uboczny, że nie pomija również zduplikowanych linii w zakresie.
mklement0
To imponujące zastosowanie seda.
Travis Rodman
3

Korzystanie z Perla

$ cat delete_5lines.txt
1
2
3
4
5 hello
6
7
8
9
10
11 hai
$ perl -ne ' BEGIN{$y=1} $y=$.  if /hello/ ; print if $y==1 or $.-$y > 5 ' delete_5lines.txt
1
2
3
4
11 hai
$
stack0114106
źródło
2

To rozwiązanie umożliwia przekazanie „n” jako parametru i odczytanie wzorców z pliku:

awk -v n=5 '
    NR == FNR {pattern[$0]; next}
    {
        for (patt in pattern) {
            if ($0 ~ patt) {
                print # remove if you want to exclude a matched line
                for (i=0; i<n; i++) getline
                next
            }
        }
        print
    }
' file.with.patterns -

Plik o nazwie „-” oznacza stdin dla awk, więc jest odpowiedni dla twojego potoku

glenn jackman
źródło
2
awk może być dużo bardziej podobny do perla, niż sądziłem!
Martin DeMello