Jak mogę usunąć cały tekst między zagnieżdżonymi nawiasami klamrowymi w wielowierszowym pliku tekstowym?

9

To pytanie pochodzi od: Jak mogę usunąć cały tekst między nawiasami klamrowymi w pliku tekstowym zawierającym wiele linii? (dokładnie tak samo, ale bez wymagań dotyczących zagnieżdżania).

Przykład:

This is {
{the multiline
text} file }
that wants
{ to {be
changed}
} anyway.

Powinno stać się:

This is 
that wants
 anyway.

Czy można to zrobić za pomocą komendy bash w jednym wierszu (awk, sed, perl, grep, cut, tr ... itd.)?

Sopalajo de Arrierez
źródło

Odpowiedzi:

13
$ sed ':again;$!N;$!b again; :b; s/{[^{}]*}//g; t b' file3
This is 
that wants
 anyway.

Wyjaśnienie:

  • :again;$!N;$!b again

    Odczytuje to cały plik.

    :againto etykieta. Nczyta w następnym wierszu i $!Nczyta w następnym wierszu pod warunkiem, że nie jesteśmy jeszcze w ostatnim wierszu. $!b againwraca do againetykiety pod warunkiem, że nie jest to ostatni wiersz.

  • :b

    To definiuje etykietę b.

  • s/{[^{}]*}//g

    Spowoduje to usunięcie tekstu w nawiasach klamrowych, o ile tekst nie będzie zawierał nawiasów wewnętrznych.

  • t b

    Jeśli powyższe polecenie zastąpienia spowodowało zmianę, wróć do etykiety b. W ten sposób polecenie zastępcze jest powtarzane, aż wszystkie grupy nawiasów zostaną usunięte.

John1024
źródło
3

Podejście Perla:

$ perl -F"" -a00ne 'for (@F){$i++ if /{/; $i||print; $i-- if /}/}' file
This is 
that wants
 anyway

Wyjaśnienie

  • -a: Włącza automatyczne dzielenie na separatora pliku podanego przez -Fdo @Ftablicy.
  • -F"": ustawia separator pól wejściowych na pusty, co spowoduje, że każdy element @Fbędzie jednym ze znaków wejściowych.
  • -00: włącz „tryb akapitowy”, w którym „wiersz” jest zdefiniowany jako dwa kolejne znaki nowej linii. Oznacza to, że cały plik w tym przypadku będzie traktowany jako pojedynczy wiersz. Jeśli plik może zawierać wiele akapitów, a nawiasy mogą obejmować wiele akapitów, użyj -0777zamiast tego.
  • -ne: przeczytaj plik wejściowy i zastosuj skrypt podany przez -edla każdej linii.

Sam skrypt jest w rzeczywistości dość prosty. Licznik jest zwiększany o jeden za każdym razem, gdy {jest widoczny i zmniejszany o jeden dla każdego }. Oznacza to, że gdy licznik ma wartość 0, nie znajdujemy się w nawiasach kwadratowych i powinniśmy wydrukować:

  • for (@F){}: zrób to dla każdego elementu @F, każdego znaku w linii.
  • $i++ if /{/;: przyrost $io jeden, jeśli ta postać to{
  • $i||print;: drukuj, chyba że $ijest ustawiony (0 jest liczone jako nieustawione)
  • $i-- if /}/: zmniejszenie $io jeden, jeśli ta postać to}
terdon
źródło