Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

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"
share|improve this question

4 Answers 4

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
share|improve this answer

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.

share|improve this answer

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.

share|improve this answer

This might work for you (GNU sed):

sed 's/([0-9]\+):/&\n/;h;s/\n.*//;y/\\/\//;s/[^/]*/\/enlistments/;G;s/\n.*\n//' file
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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