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.

I have a series of HTML files that contain two lines like this:

<body>
<h1>Title</h1><p>
<a href="url">Description</a><br>

I want to replace this text with something else using a bash script. I'm trying

sed -i -r 's/<h1>Title.*?$\/^.*?<br>/Replacement text/1' filename.html

but it is not working. I'm suspecting it is getting stuck on the new line and not knowing how to go around the problem.

Any help appreciated. Feel free to suggest other Linux tools other than sed as long as it works!

share|improve this question
1  
Do you really need sed for this? sed reads line-by-line, so replacing multi-line text is a little tedious there. –  slhck May 19 '13 at 17:12
    
I don't have to use sed. I'm open to other linux commands. –  To Do May 19 '13 at 17:17
    
yeah a pattern crossing multiple lines may be an issue for sed. –  barlop May 19 '13 at 17:23

3 Answers 3

up vote 6 down vote accepted

I'd use Perl for this:

perl -0pe 's/<h1>Title.*\n.*<br>/replacement/' filename.html

Here, -0 makes Perl split records on the NUL character instead of reading line-by-line, which is the default when using the -p option.

With Perl regular expressions you need .* to match any character multiple times, and you match the newline with \n.

Example:

$ echo '<body>
<h1>Title</h1><p>
<a href="url">Description</a><br>' | perl -0pe 's/<h1>Title.*\n.*<br>/replacement/'
<body>
replacement
share|improve this answer
    
And don't forget, never use regular expressions to parse HTML! :) –  terdon May 19 '13 at 17:47
1  
This is almost good. I added a ? after the two .* to disable greediness. Is it possible to make the command modify the file instead of outputting to STDOUT? –  To Do May 19 '13 at 21:07
2  
@ToDo Perl can do in-place editing with the -i option, so run perl -0p -i~ -e 's/…/…/' to have it edit the original file, creating a backup copy with ~ as suffix. Or, to replace without backup file, call perl -0pi -e …. –  slhck May 19 '13 at 21:30
    
Thanks, that did the trick. –  To Do May 19 '13 at 22:05

sed cannot match more than one line directly. When multiline pattern is needed, reach for a more powerful tool like Perl:

perl -i~ -ne 'if (/^<h1>Title/) {
                  $n = <>;
                  if ($n =~ /<br>$/) { print "Replacement\n" }
                  else { print "$_$n" }
              } else { print }'
share|improve this answer

This can be done with sed.

sed -nf repl.sed filename.html

where repl.sed contains:

# Must have one line loaded up before branching to rep.
# Processing will start this way.
:rep
# Load extra line into pattern space
N
# Test for title
/<h1>.*<\/h1><p>\n<a href=".*">.*<\/a><br>/{
  #Substitute and print
  s/<h1>\(.*\)<\/h1><p>\n<a href=".*">.*<\/a><br>/Title: \1/p
  #append next line without cycling
  N
  # everything but the last line
  s/.*\n\([.\n]*\)/\1/
  #test for last line
  ${
    p
    # this will effectively end the program
    n
  }
  b rep
}
${
  # will print pattern space (both lines)
  p
  # this will effectively end the program
  n
}
#Print first line in pattern space
P;
#Remove first line in pattern space with newline
s/.*\n\([.\n]*\)/\1/
b rep

See Working with Multiple Lines

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.