4

How can I convert this:

foo (blah) (bar 80)
foo (cats) (blat 92)

To this:

foo bar 80
foo blat 92

I would like to keep all the text that is NOT in parentheses.

I only want to extract text between parentheses that contain the strings "bar" or "blat" in them.

Pairs of parentheses that do not contain "blat" or "bar" should be ignored.

Sed or awk will be much preferred

  • I'm not sure I understand. Do you want to keep all text that is not in parentheses and, of the strings in parentheses, keep only those that start with blat or bar? What if blat or bar is somewhere else in the parenthesis? Can there be spaces between the parentheses and the strings? – terdon Apr 29 '15 at 17:01
  • Yes terdon, I would like to keep all the text that is NOT in parentheses. Pairs of parentheses that do not contain blat or bar should be ignored. They do not have to start with blat or bar as long as they are inside the parentheses, and yes a space is preferred between the parentheses and the string. – TuxForLife Apr 29 '15 at 17:04
3

While this is indeed possible in sed or awk, it is much easier (for me, anyway) using Perl:

$ perl -ple '@pars=( /(\(.+?\))/g ); 
              for $par (@pars){
                s/\s*.$par.// unless $par=~/blat|bar/
              } s/[()]//g;' file
foo bar 80
foo blat 92

Explanation

  • -ple : print each line of the input file, after executing the script on it. The -l removes trailing newlines and adds a newline to each call of print.
  • @pars=( /(\(.+?\))/g ); : the @pars array now contains all strings that are within parentheses.
  • for $par (@pars){ ... } : for each of the strings found above.
  • s/\s*.$par.// unless $par=~/blat|bar/ : remove this set of parentheses if they don't contain blat or bar.
  • s/[()]//g; : Remove all parentheses (not the text within them).

You could also golf condense the above to

perl -ple 'for$par((/(\(.+?\))/g)){$par=~/blat|bar/||s/\s*.$par.//};s/[()]//g;' file
  • Great answer and very well explained. Was Perl preferred because the question is a little too complex for sed/awk to do? – TuxForLife Apr 29 '15 at 18:04
  • 1
    @user264974 Perl was preferred first and foremost because I prefer Perl, so it's much easier for me. That said, yes, while you could play around with the hold space and try to match the different strings in sed, that would make for a very long and cryptic sed command. Awk would be even worse since it really is not designed for this kind of thing. It wants its data in columns. – terdon Apr 29 '15 at 18:11
  • 1
    @user264974 I take that back. I hadn't realized that sed can deal with non-grouping parentheses but apparently it can. – terdon Apr 30 '15 at 13:12
5

Using sed:

< inputfile sed 's/(\([^\)]*\(bar\|blat\)[^\)]*\))/\1/g; s/(.*) //g'

Input file:

test (bar) (blat)
bar (testblat) (bartest)
blat (testbar) (barblat) (no) (blatanother)

Output file:

test bar blat
bar testblat bartest
blat testbar barblat blatanother

Breakdown:

#1:

  • (: matches a ( character
  • \(: starts grouping the capturing group
  • [^\)]*: matches 0 or more characters not )
  • \(: starts grouping the allowed strings
  • bar: matches the 1st allowed string
  • \|: separates the 2nd allowed string
  • blat: matches the 2nd allowed string
  • \): stops grouping the allowed strings
  • [^\)]*: matches 0 or more characters not )
  • \): stops grouping the capturing group
  • ): matches a ) character

#2:

  • (: matches a ( character
  • .*: matches 0 or more characters
  • ): matches a ) character
  • : matches a character
  • 1
    @terdon I didn't understood he wanted to remove non-matching pairs of parenthesis as well, thanks. Updated. – kos Apr 30 '15 at 13:10
3

Using python:

#!/usr/bin/env python2
import re
with open('/path/to/file.txt') as f:
    for line in f:
        pat_list = re.findall(r'\(([^)]*?)\)', line.rstrip())
        for pat in pat_list:
            if not re.search(r'(?:blat|bar)', pat):
                print re.sub(r'\(|\)', '', line.replace(' ({0})'.format(pat), '').rstrip())

Output:

foo bar 80
foo blat 92
  • Here we have used the re (Regular Expression) module of python.
  • pat_list will contain the list of strings within parentheses
  • Then we have searched for the presence of "blat" or "bar" in the pat_list members
  • If not found, we have printed the line removing unnecessary portionss including parentheses.
  • @TuxForLife: Thanks..yeah..i am interested..i will mail you..lets remove the comments :) – heemayl May 31 '15 at 17:28
3

Using awk:
Save the following code into a text file and make it executable (chmod u+x filename).

Then, run it like this:

awk -f filename inputfile

This is huge compared to solutions in perl or python, I'm adding this just because awk or sed was the preferred solution and to show that it's possible to use awk even though it's not convenient.

{
#list of words to look for in parentheses: (named "w" to speed up adding items)
w[0] = "bar";
w[1] = "blat";

#"bool" value whether of not to crop spaces around omitted parenthesis with their content
cropSpaces = 1;

spaces = 0;                     #space counter used for cropping 
open = 0;                       #open/nested parenthesis counter
st = 0;                         #marks index where parenthesis starts
end = 0;                        #marks index where parenthesis ends
out = 0;                        #"bool" value indicating whether or not the word has been found
for(i = 1;i-1 < length($0);i++){     #for each character
  c = substr($0,i,1);                 #get character
  if(c == "("){                       #character is '('
    open++;                            #increment parenthesis counter
    if(open == 1) st = i+1;            #marks start of parenthesis (if not nested)
  }
  else if(c == ")"){                 #char is ')'
    open--;                           #decrement parenthesis counter
    if(open == 0) end = i;            #mark end of parenthesis (if not nested)
  }
  else{                             #any other char
    if(open == 0){                   #outside of parenthesis
      if(cropSpaces && c == " "){     #char is space (and cropSpaces option is not 0) 
        if(spaces == 0) printf c;      #print space if not sequential  
        spaces++;                      #increment space counter
      }
      else{                           #any other char
        spaces = 0;                    #set previous spaces counter to 0
        printf c;                      #print char
      }
    }
    else if(!out){                   #inside of parenthesis (and no word has been found)
      for(j = 0; j < length(w); j++){               #for every word in list
        if( substr( $0,i,length(w[j]) ) == w[j]){    #if word matches
          out = 1;                                    #word has been found
          break;                                      #do not look for any other words
        }
      }
    }
  }
  if(open == 0 && out){              #outside of parenthesis and word found in previous parenthesis
    printf substr($0,st,end-st);      #print content
    out = 0;                          #reset "word found" indicator 
    spaces = 0;                       #reset spaces counter
  }
}

printf "\n";                        #print newline
}
  • I do not see any output. – A.B. Apr 30 '15 at 12:44
  • 1
    @A.B. the awk call was wrong, you need awk -f to run the script. Try the updated version. – terdon Apr 30 '15 at 12:47
  • Works and +1 for the effort. Where you learn that? ;) – A.B. Apr 30 '15 at 12:49
  • 1
    @A.B. Oh, my bad, sorry for wrong command... I'm studying informatics on CTU in Prague and awk scripting was part of obligatory "Programming in Shell 1" course (in first semester). – curusarn Apr 30 '15 at 15:22
  • I can see why perl/python is preferred in this situation. That is quite the code, thank you for the grand effort. Kos' sed solution is really short, I actually expected awk to be shorter – TuxForLife May 1 '15 at 15:59
0

little late but, what about this, power of oneline simplicity:

> cat test.py
from string import replace

stuff = '''
foo (blah) (bar 80)
foo (cats) (blat 92)
'''

for i in stuff.split('\n'):  # split by \n
  if i != str():  # not empty string
    print ''.join(i.split()[0]+' '+i.split()[2]+' '+i.split()[3]).replace('(','').replace(')','')

>>> python test.py 
foo bar 80
foo blat 92

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.