xenakaaii
xenakaaii

Reputation: 75

sed substitute and show line number

I'm working in bash trying to use sed substitution on a file and show both the line number where the substitution occurred and the final version of the line. For a file with lines that contain foo, trying with

sed -n 's/foo/bar/gp' filename

will show me the lines where substitution occurred, but I can't figure out how to include the line number. If I try to use = as a flag to print the current line number like

sed -n 's/foo/bar/gp=' filename

I get

sed: -e expression #1, char 14: unknown option to `s'

I can accomplish the goal with awk like

 awk '{if (sub("foo","bar",$0)){print NR $0}}' filename

but I'm curious if there's a way to do this with one line of sed. If possible I'd love to use a single sed statement without a pipe.

Upvotes: 1

Views: 1799

Answers (2)

Jeff Bowman
Jeff Bowman

Reputation: 95754

I can't think of a way to do it without listing the search pattern twice and using command grouping.

sed -n "/foo/{s/foo/bar/g;=;p;}" filename

EDIT: mklement0 helped me out there by mentioning that if the pattern space is empty, the default pattern space is the last one used, as mentioned in the manual. So you could get away with it like this:

sed -n "/foo/{s//bar/g;=;p;}" filename

Before that, I figured out a way not to repeat the pattern space, but it uses branches and labels. "In most cases," the docs specify, "use of these commands indicates that you are probably better off programming in something like awk or Perl. But occasionally one is committed to sticking with sed, and these commands can enable one to write quite convoluted scripts." [source]

sed -n "s/foo/bar/g;tp;b;:p;=;p" filename

This does the following:

  • s/foo/bar/g does your substitution.
  • tp will jump to :p iff a substitution happened.
  • b (branch with no label) will process the next line.
  • :p defines label p, which is the target for the tp command above.
  • = and p will print the line number and then the line.
  • End of script, so go back and process the next line.

See? Much less readable...and maybe a distant cousin of :(){ :|:& };:. :)

Upvotes: 4

Ed Morton
Ed Morton

Reputation: 204558

It cannot be done in any reasonable way with sed, here's how to really do it clearly and simply in awk:

awk 'sub(/foo/,"bar"){print NR, $0}' filename

sed is an excellent tool for simple substitutions on a single line, for anything else use awk.

Upvotes: 1

Related Questions