2

I want to find and replace a pattern but it also wraps around to the next line as well, I need to find some way to handle this

Something that looks like:

....tr("Changes to output mappings will be deleted. "
"There is no undo available. Do you want to continue?"));

to turn into something like

...."Changes to output mappings will be deleted. "
"There is no undo available. Do you want to continue?");

(the ... is previous code)

So far, I have code such as (works for a single line but not wrapped text/code):

sed -i 's/tr(\([^)]*\))/\1/I' $file_t

How would I go about this?

4

For the case where the tr( commands are always spread over two lines, try:

$ sed  '/tr(/N; s/tr(\([^)]*\))/\1/I' "$file_t"
...."Changes to output mappings will be deleted. "
"There is no undo available. Do you want to continue?");

The only change here is the addition of /tr(/N. For any line that matches tr(, this reads in the next line and adds it to the pattern space (N).

To change the file in place, use -i:

sed  -i.bak '/tr(/N; s/tr(\([^)]*\))/\1/I' "$file_t"

More robust version

If it is possible that tr(...) commands may appear on one line, on two lines, or more than two lines, we need to change the logic a bit:

sed  '/tr(/{ :a; s/tr(\([^)]*\))/\1/I; t; N; ba}' filename

This looks for lines that contain tr(. For any such lines, the commands in curly braces, {...}, are performed. For those lines, the substitution is first attempted on that line only. If the substitution happens, then the t command tells sed that we have completed this line. If it didn't happen, meaning that there was no closing ), then the next line is read in (N) and sed branches back to label a (ba) and the substitution is tried again. This repeats until enough lines are read in to find the ).

As an example, here is a file with tr(...) spread over one, two, and three lines:

$ cat filename
tr("abc");
....tr("Changes to output mappings will be deleted. "
"There is no undo available. Do you want to continue?"));
tr("one
two
three");

Here is the output:

$ sed  '/tr(/{ :a; s/tr(\([^)]*\))/\1/I; t; N; ba}' filename
"abc";
...."Changes to output mappings will be deleted. "
"There is no undo available. Do you want to continue?");
"one
two
three";

Distinguishing between tr(...) and Ptr(...)

To make sure that we substitute only for tr and not for Ptr or Str, we need to make sure that tr starts at a word boundary. With GNU sed (Linux), a word boundary is denoted \b:

sed  '/\btr(/{ :a; s/\btr(\([^)]*\))/\1/I; t; N; ba}' filename

Using BSD (OSX) sed, the following might work:

sed  '/[[:<:]]tr(/{ :a; s/[[:<:]]tr(\([^)]*\))/\1/I; t; N; ba}' filename
5
  • /tr(/{s///;:a;/)/! {n;ba};s///}
    – Costas
    Aug 26 '16 at 18:32
  • wow, very good explanation and solution. Thank you
    – user186523
    Aug 26 '16 at 19:28
  • What about if there is something such as Ptr() or Str() that contains that tr string, is there any way to exclude those as well??
    – user186523
    Aug 26 '16 at 21:22
  • @user186523 Yes, that can be done. I just added code for that to the end of the answer.
    – John1024
    Aug 26 '16 at 21:56
  • how about perl -0777 -pe 's/\btr\(([^)]*)\)/$1/g' filename
    – Sundeep
    Aug 27 '16 at 8:08

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.