3

I would like to know who to remove a string or paragraph that are between ((( string )))

Lorem ipsum (((dolor sit amet))), consectetur adipiscing elit. Vestibulum aliquet fringilla est, dictum tempor nunc venenatis at. Sed nec velit sit amet velit cursus imperdiet. Vivamus tincidunt ut nunc quis euismod. Quisque sit amet lorem rhoncus, malesuada justo at, ullamcorper erat.

So "dolor sit amet" should not be in the return

Here's the cmd I have for now which detect the first ((( but then stop...

sed -e "/(((/,/)))/d" file.txt
jimmij
  • 47,140
Warface
  • 133
  • Do you want to keep the ((( and ))) in the text? – glenn jackman Mar 10 '15 at 14:53
  • How complex can this get? Can there be nested parentheses? Things like ((( foo ((( bar))) baz)))? How would you like those to be dealt with? How about ((( foo \n bar (((baz))) )))? – terdon Mar 10 '15 at 15:04
  • No actualy I need to remove what' inside those. And if there's line break it need to be aware of that too. – Warface Mar 10 '15 at 15:05
  • @terdon No there's no nested ((( ))). But it have line breaks so it should be able to search on other lines to find the closing parentheses. – Warface Mar 10 '15 at 15:14

3 Answers3

2
sed -e :p -e '/(((/!b     
'   -e :n -e 's/)))/\     
/;            s/(((.*\n//; tp
$d;N;         s//(((/;     tn'

This should do it. It will branch away (and consequently autoprint) any lines not matching ((( but once one is found it attempts to remove everything between the first occurring ((( sequence and the first occurring ))). If it cannot because the trailing ))) is not found on the current line then it pulls in the Next line, removes everything between the ((( and the head of the next line, and searches again. If it makes it to the end of the $last line while still searching for ))) it gives up. In this way it never buffers more than a line at a time because it removes all that follows ((( each time it has to pull in a newline.

It should handle as many ((( ))) pairs as might occur on a line - and it does not matter if any ( or ) occur between the two ends - it will seek past 2 or fewer ) and any number of (.

After finding ))) it resets to a search for ((( and so it doesn't fail to handle the next pair even after crossing new-line boundaries.

  1. :p - declare the p branch label. The script branches here if it can replace a ))) sequence with a newline then subsequently remove everything between ((( and \n.
  2. /(((/!b - branch away - and autoprint pattern space - if there are no remaining ((( sequences in pattern space.
  3. :n - declare the branch :label n. The script branches here if a ((( is found but a ))) cannot be found on the same line.
  4. s/)))/\n/ - substitute first occurrence of ))) for a newline. This only happens if a ((( has already been matched.
  5. s/(((.*\n// - substitute away everything between first ((( and the only \newline in pattern space.
  6. tp - test for a successful substitution; if true, branch to label :p.
  7. $d;N - the last substitution was not successful; if current line is the $last delete it, else append the Next to pattern space.
  8. s//(((/;tn - repeat last regexp and substitute all between first occurring ((( and the newline just added for ((( then branch to label :n.
mikeserv
  • 58,310
1

Try

sed 's/((([^)]*)))//' file

or probably even better in your sentence

sed 's/ ((([^)]*)))//' file
jimmij
  • 47,140
1

Doing this for single line strings is very simple:

sed 's/((([^)]*)))//g' file

If you need it to deal with multiline strings, it gets more complex. One approach would be to use tr to replace all newlines with the null character (\0), make the substitution and translate back again:

tr '\n' '\0' < file | sed 's/((([^)]*)))//g' | tr '\0' '\n'

Alternatively, you could just use perl:

perl -0pe 's/\(\(\([^)]+\)\)\)//g;' file

The -0 causes perl to read the entire file into memory (this might be a problem for huge files), the -p means "print each line" but because of the -0, the "line" is actually the entire file. The s/// is the same idea as for sed.

terdon
  • 242,166