2

I realise sed can pattern match over multiple lines. Is it possible to replace a word within a certain section of these multiple lines? E.g.

file begins
random line
>>starting line block
    random line
    random line
    .....
    >>replace from here
     hello
     hi there hello
    >>stop replacing here
    more random stuff
>>finishing line block 
more random lines
file ends

Suppose I want to replace 'hello' with 'bye' but only between 'replace from here' to 'stop replacing here' and also only in between 'starting line block' to 'finishing line block'. Is it possible to do that in place using sed? e.g.

sed '/starting line block/,/finishing line block/ /replace from here/,/stop replacing here/ s/hello/bye/g/'

Dana
  • 310
  • 3
  • 13
  • have you read all the postings here about "don't use `sed` to parse `XML` ? I think you're trying the same thing here. `awk` can do what you have specified pretty easily, but you may run into corner cases that need extra babying. Good luck. – shellter Mar 11 '15 at 02:53
  • This might be something to look at. http://stackoverflow.com/questions/18682503/print-xml-element-with-awk . Else search for `[awk] xml` – shellter Mar 11 '15 at 03:02

1 Answers1

3

You can use blocks for this, as in the following sed script:

/start/,/end/ {
    /from/,/to/s/foo/bar/g
}

Run it with sed -f <script> "$file".

The {} brackets delimit a block of commands, and /start/,/end/ is the range of lines the commands will be run for. In this case we have a single command, s/foo/bar/g, which we run for each line in the range /from/,/to/ within each block.

The above will run s/foo/bar/g on the lines with the from and to delimiters too. I'm guessing that should be okay.

This also works if multiple line ranges match /start/,/end/ and /from/,/to/.

The following one-liner also works with GNU and (at least one) BSD sed. POSIX does not specify ; for separating commands though, and wants a newline before the closing } (link to spec.), so it might be less portable.

sed '/start/,/end/{/from/,/to/s/foo/bar/g;}' "$file"

You should be careful if you're parsing XML or HTML as some people in the comments pointed out. Here's one answer that seems to be pretty famous, though there's also this. Pragmatism might win in the end depending on your requirements and input data, especially for one-offs.

Community
  • 1
  • 1
Ulfalizer
  • 4,664
  • 1
  • 21
  • 30
  • Nicely done; to also make _BSD_ `sed` happy, you need a `;` before the closing `}`. – mklement0 Mar 11 '15 at 03:50
  • yes, well done. Can you please add output of `sed --version ; uname -srv` ? (I don't think this would work with Sun or AIX). Good luck to all. – shellter Mar 11 '15 at 19:45
  • @shellter: This is with **GNU sed 4.2.2**. Command blocks are specified by POSIX (see http://pubs.opengroup.org/onlinepubs/7908799/xcu/sed.html), but from a quick look it does not seem to support `;` for separating commands (it wants newlines) and also wants a newline before the closing `}`. That might make it cleaner to use a separate script file and pass it with `sed -f`. It would be interesting to know if the one-liner works on those other systems if you have access to them. – Ulfalizer Mar 11 '15 at 19:59
  • @shellter: I added what I think should be a POSIXly portable version to the answer. – Ulfalizer Mar 11 '15 at 20:13
  • That link was for SUSv2 apparently. SUSv4 is at http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html. Nothing that would be relevant here seems to have changed though. – Ulfalizer Mar 11 '15 at 20:43