How can I delete a string inside parentheses using sed, inside a sed script?

Viewed 128

I would like to remove text inside parentheses (including the parentheses) using sed in a sed script. For example, I would like to delete the phrase (Chris Pratt) and (Chris-Pratt) and keep (Chris_Pratt). (They are both on the same line). And do this for the entire file. For example, the line looks like this:

Star Lord (Chris Pratt), (Chris-Pratt), age 42, actor, (Chris_Pratt)

This is what I would want to to look like after the sed command in a sed script:

Star Lord, age 42, actor, (Chris_Pratt)

That's what I want to do with every single line (there are multiple lines with other names).

I have already tried:

s/[(][^)]*[)]//g

This one works, but it also deletes the parentheses including the underscore, also:

s/\([[:alpha:]]{1,} [[:alpha:] ]{1,}\)\ //g

This one does work when I run it with sed normally in the command line, but it doesn't work when I run it in a script for some reason.

5 Answers

You can use

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

Same pattern with POSIX ERE syntax:

sed -E 's/ *\([^()_]*\)//g' file > outputfile

Details:

  • *
  • ( - a literal ( char (since it is a POSIX BRE pattern), when using POSIX ERE, \( must be used
  • [^()_]* - zero or more chars other than (, ) and _
  • ) - a literal ) char (since it is a POSIX BRE pattern), when using POSIX ERE, \) must be used.

See the online demo:

#!/bin/bash
s='Star Lord (Chris Pratt), age 42, actor, (Chris_Pratt)'
sed 's/ *([^()_]*)//g' <<< "$s"
# => Star Lord, age 42, actor, (Chris_Pratt)
sed -E 's/ *\([^()_]*\)//g' <<< "$s"
# => Star Lord, age 42, actor, (Chris_Pratt)

Demo screenshot:

enter image description here

1st solution(sed solution): With your shown samples, please try following sed program. Using sed's backreference capability here.

sed -E 's/(^[^(]*) \([^)]*\), \([^)]*\)(.*)/\1\2/' Input_file

Explanation: Using sed's -E option which enables ERE(Extended regular expressions) in our program here. Then in main program using s option of sed to perform substitution operation. We are mentioning (^[^(]*) \([^)]*\), \([^)]*\)(.*) which is creating 2 back references here(a temp space in memory to retrieve caught values later in program). While substituting using \1(first backreference) and \2(2nd back reference) to get the expected output mentioned by OP.

Explanation of regex:

(^[^(]*)               ##Creating 1st capturing group which captures values from starting of line to till 1st occurrence of ( here.
 \([^)]*\), \([^)]*\)  ##Matching space ( till next occurrence of ) here followed by comma then space followed by ( till next occurrence of ).
(.*)                   ##Creating 2nd capturing group which has everything after previous match.


2nd solution(awk solution): Added an awk solution here, by using match function of awk. regex used is same as mentioned above, so simple explanation for this program would be; match not needed part by this function and print only required part.

awk '
match($0,/[[:space:]]*\([^)]*\),[[:space:]]*\([^)]*\)/){
  print substr($0,1,RSTART-1) substr($0,RSTART+RLENGTH)
}
' Input_file

This might work for you (GNU sed):

sed -E ':a;s/ *\([^()]*\),(.*\(.*\))/\n\1/;ta;s/,*\n+/,/g' file

Replace all literals within parens (including the parens) preceded by a possible space and followed by a , followed by at least one more literal within parens with a newline.

Then replace multiple newlines preceded by a possible ,'s with a single ,.

Using sed

$ sed 's/,\? ([^)][A-Za-z -]*)//g' input_file
Star Lord, age 42, actor, (Chris_Pratt)

You should clarify if you’re actually using the -E option for “extended” regex.

With -E on, parentheses (()) must be escaped (\() to match literally. Without escaping, they’re interpreted as group/field separators (without -E this is reversed).

My solutions assume -E is on.

  • For an executable sed script your shebang should look similar to #!/bin/sed -Ef or #!/usr/bin/env -Ssed -Ef

  • For a non executable sed script, invoke it like: sed -Ef my-script

Solution:

If the lines all have the same layout, use this:

s/([[:space:]]+\([^)]+\),){2}/,/

If the layout varies, try on of these:

s/[[:space:]]*\([^)]*[- ][^)]+\)//
s/[[:space:]]*\([^)]*[- ][^)]+\),?//g

All parenthesised fields containing a space and/or dash are removed, also leading whitespace and a trailing comma (if either is present). For the first match only, any comma is preserved.

s/[[:space:]]*\([^)_]+\)//
s/[[:space:]]*\([^)_]+\),?//g

All parenthesised fields not containing an underscore are deleted (including (Madonna)). Ditto (as above) with the whitespace and commas.

Edit: I slightly misread your expected output. You would have gotten Star Lord age 42, actor, (Chris_Pratt) instead of Star Lord, age 42, actor, (Chris_Pratt) (note the first comma). I fixed the solutions accordingly.

Related