9

This question comes from How can I delete all text between curly brackets in a multiline text file? (just the same, but without the requirements for nesting).

Example:

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

Should become:

This is 
that wants
 anyway.

Is it possible to do this with some sort of one-line bash command (awk, sed, perl, grep, cut, tr... etc)?

2 Answers2

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

Explanation:

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

    This reads in the whole file.

    :again is a label. N reads in the next line and $!N reads in the next line on the condition that we are not already at the last line. $!b again branches back to the again label on the condition that this is not the last line.

  • :b

    This defines a label b.

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

    This removes text in braces as long as the text contains no inner braces.

  • t b

    If the above substitute command resulted in a change, jump back to label b. In this way, the substitute command is repeated until all brace-groups are removed.

John1024
  • 74,655
3

A Perl approach:

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

Explanation

  • -a : turns on automatic splitting on the file delimiter given by -F into the @F array.
  • -F"" : sets the input field separator to empty which will result in each element of @F being one of the input characters.
  • -00 : turn on "paragraph mode", where a "line" is defined as two consecutive newline characters. This means that the entire file in this case will be treated as a single line. If your file can have many paragraphs and the brackets can span multiple paragraphs, use -0777 instead.
  • -ne : read an input file and apply the script given by -e to each line.

The script itself is actually quite simple. A counter is incremented by one every time a { is seen and decremented by one for every }. This means that when the counter is 0, we are not inside brackets and should print:

  • for (@F){} : do this for each element of @F, each character in the line.
  • $i++ if /{/; : increment $i by one if this character is a {
  • $i||print; : print unless $i is set (0 counts as unset).
  • $i-- if /}/ : decrement $i by one if this character is a }
terdon
  • 242,166