6

Given this:

echo AAA | sed -r 's/A/echo B/ge'

I get this:

Becho Becho B

I would have thought I would get "BBB". This is with GNU sed version 4.2.1. What is going on, and how can I use the execute flag, and have multiple replacements can occur on one line (from the shell, not from perl et al)?

6

The flags work together in the opposite way to what you're expecting. The documentation of /e is, for the record:

This command allows one to pipe input from a shell command into pattern space. If a substitution was made, the command that is found in pattern space is executed and pattern space is replaced with its output. A trailing newline is suppressed; results are undefined if the command to be executed contains a nul character. This is a GNU sed extension.

That is a bit tortuously written. What it means is that, after the completion of a s/// command for this line, if there was a change, the (new) line is executed as a command and its output used as the replacement for this line.

So for your given command:

echo AAA | sed -r 's/A/echo B/ge'

it first replaces each A with echo B, and then executes the result as a command. It has (roughly speaking) the same effect as:

echo AAA | sed -r 's/A/echo B/g' | sh

GNU sed does not directly support the mode you want, although you can fake it with a more complex script if desired. Alternatively, Perl's /e modifier to its s command does have the behaviour you're looking for, but with Perl expressions instead.

  • Aha - executing the command it finds in the pattern space after processing the whole line! OK that sort of makes some sense. Although Perl does what I wanted with those two flags set. Thx. – spinkus Jan 24 '15 at 5:46
4

You are getting multiple replacement, but you don't get multiple executions. The pattern is executed once all the replacements have been made.

Without the e flag the result of

echo AAA | sed -r 's/A/echo B/g'

is

echo Becho Becho B

So that's the command line that's executed if you do include the e flag, which is equivalent to

echo 'Becho Becho B'
2

To get 'BBB' from 'AAA' using GNU sed's 's' command with the 'e' flag, one can do this:

echo AAA | sed -re 's/A/echo -n B;/ge'

The 'AAA' is substituted to 'echo -n B;echo -n B; echo -n B;', which, when eventually executed, leads to 3 sequentially running echo commands, one for each of the global matches. The '-n' omits the linefeed characters from echo's output, making the 'B's end up on one line (but sed appends one linefeed by itself when finally printing the pattern space).

0

A few things:

(1) The exact use case you describe can be handled with just

echo AAA | sed 's/A/B/g'

(2) For execution, if that is specifically what you want (for a more advanced usage than just echoing), you can use the e flag as the last step:

echo AAA | sed -r 's/A/B/g;s/(.*)/echo \1/e'

(3) If you want to only execute the command if the FIRST substitute command substituted something, use a branch:

echo AAA | sed -r 's/A/B/g;te;b;:e;s/(.*)/echo \1/e'

These only work in GNU sed as written above. BSD sed (at least the version I have) doesn't support the -r option, and requires an end-of-string to follow a branch name. (You can work around this last point by adding multiple -e options with arguments.)

Note that with the given input (AAA) these three one-liners are all functionally identical. But if the echo command were changed to something else, or if the -n option were passed to sed, you'd see the difference.

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.