2

Using sed how could I replace two patterns within a larger pattern on a single line?

Given a single line of text I want to find a pattern (Let's call this the outer pattern) and then within that outer pattern replace two inner patterns.

Here's a one line example of the input text:

Z:\source\private\main\developer\foo\setenv.sh(25):     export 'FONTCONFIG_PATH'="$WINE_SHARED_SUPPORT/X11/etc/fonts"

In the example above the outer pattern is /^.*\([[:digit:]]+\):/ which should equal Z:\source\private\main\developer\foo\setenv.sh(25): The two inner patterns are /^[A-Za-z]:/ and /\\/.

Another way to phrase my question is:

Using sed I know how to perform replacements of a pattern using the s command, but how do I limit the range of s command so it only works on the portion of the input string up to the (25):?

The ultimate result I am trying to get is the line of text is transformed into this:

/enlistments/source/private/main/developer/foo/setenv.sh(25):     export 'FONTCONFIG_PATH'="$WINE_SHARED_SUPPORT/X11/etc/fonts"
gronostaj
  • 57,004
HairOfTheDog
  • 2,442

4 Answers4

1

This command converts your input example to your output example:

sed 's|\\|/|g;s|^[^/]*|/enlistments|'

If some of your input contains backslashes in the portion after the /^.*([[:digit:]]+):/ part, then you will have to divide and conquer to prevent those latter backslashes from being replaced.

sed 'h;s/^.*([[:digit:]]\+)://;x;s/^\(.*([[:digit:]]\+):\).*/\1/;s|\\|/|g;s|^[^/]*|/enlistments|;G;s/\n//'

Explanation (steps marked with an asterisk ([*]) apply to both commands):

  • h - copy the line to hold space
  • s/^.*([[:digit:]]\+):// - delete the first part of the line from the original in pattern space
  • x - swap pattern space and hold space
  • s/^\(.*([[:digit:]]\+):\).*/\1/ - keep the first part of the line from the copy (discard the last part)
  • s|\\|/|g - [*] change all the backslashes to slashes (in the divide-and-conquer version, only the portion in pattern space - the first part of the line - is affected)
  • s|^[^/]*|/enlistments| - [*] change whatever appears before the first slash into "/enlistments" - this could be made more selective if needed
  • G - append a newline and the contents of hold space onto the end of pattern space
  • s/\n// - remove the interior newline
0

There are two facts that I think you'll find useful:

  1. By default, sed will only replace the first occurrence of a string in a line. So given the following line:

    foo foo
    

    The command sed s/foo/bar/ will result in:

    bar foo
    
  2. You can limit a sed command to lines matching a specific pattern. If you want to perform the above substitution only on lines containing the string "xyz", you would use a command like this:

    sed '/xyz/ s/foo/bar/'
    

I think this should help you do what you want.

larsks
  • 4,212
0

You could try

sed -r 's/^[A-Z]:\\|\\/\//g' | sed 's/^/\/enlistments/'

Now, this of course requires that there's no backslashes in the part after (25): to work. But as I suspect this will never be the case due to unix path structure this will never be the case.

JaHei
  • 834
  • 6
  • 12
0

This might work for you (GNU sed):

sed 's/([0-9]\+):/&\n/;h;s/\n.*//;y/\\/\//;s/[^/]*/\/enlistments/;G;s/\n.*\n//' file
potong
  • 176