I need to insert text between two set patterns in a large set of files.
I need to change every line with this form:

<a href="/entry/someFile">

To this:

<a href="/entry/someFile.xhtml">

I've been trying to write a sed command to accomplish this, but I'm finding it very difficult.

I know that I need to use href="/entry/ and "> as delimiters, but I don't understand how to use sed for more complicated text insertions/substitutions.

edit: I realize I was unclear in my original post. The unchanging patterns are href="/entry/ and ">. "someFile" could be any file name.

  • 1
    Is this in an XML file that is well formed? – Kusalananda Apr 8 '18 at 18:23
  • No, its the product of a web page scrape that I'm turning into an ebook for personal use. I have to use .xhtml for the ePub editor Sigil. – Daniel Sweet Apr 8 '18 at 18:39

For a sed solution, see further down in this answer.

Assuming that the a nodes are part of a well formed XML document, and that you would want to append .xhtml to the value of their href tags when the existing values starts with /entry/:

xml ed -u '//a[starts-with(@href, "/entry/")]/@href' \
       -x 'concat(../@href,".xhtml")' file.xml >file-new.xml

This uses XMLStarlet (sometimes installed as xmlstarlet instead of just xml) and it will find the relevant a nodes and append .xhtml to their href attributes regardless where in the document they occur.

The result is saved to a new file here, but you may use xml ed --inplace ... to edit the file in place once you have made sure that it works.


$ cat file.xml
<?xml version="1.0"?>
  <a href="/entry/someFile1"/>
  <a href="/entry/someFile2"/>
  <a href="/entry/someFile3"/>

$ xml ed -u '//a[starts-with(@href, "/entry/")]/@href' -x 'concat(../@href,".xhtml")' file.xml
<?xml version="1.0"?>
  <a href="/entry/someFile1.xhtml"/>
  <a href="/entry/someFile2.xhtml"/>
  <a href="/entry/someFile3.xhtml"/>

Using sed (which you would not use on a well formed XML file usually):

sed 's|<a href="/entry/[^"]*|&.xhtml|g' file.xml

This matches the string <a href="/entry/ followed by any number of characters that are not " (this would be the filename). This whole matching part is then replace with itself and the string .xhtml.

With sed -i, this would make the modification in place.

Testing (on the same file as above):

$ sed 's|<a href="/entry/[^"]*|&.xthml|g' file.xml
<?xml version="1.0"?>
  <a href="/entry/someFile1.xhtml"/>
  <a href="/entry/someFile2.xhtml"/>
  <a href="/entry/someFile3.xhtml"/>

sed can be very complicate, but for your needs its easy to use try:

sed -i 's/<a href=".*">/<a href="/some/link/">/g' yourfile.html

The syntax here is easy:

sed -i 's/stringt before replacing/string after replacing/g'

the .* is the wildcard matches anything use it at the needed place

Maybe you should copy the file before using sed. the -i changes your file not creates a new one:

-i [SUFFIX], --in-place[=SUFFIX] edit files in place (makes backup if SUFFIX supplied)

the g at the end replaces all matches in the file

if you want to change only the first match in your file use:

sed -i '0,/<a href=".*">/{s/<a href=".*">/<a href="/some/link/">/}' yourfile.html

same syntax:

sed -i '0,/string before/{s/string brefore/string after/}'
  • 1
    The issue with your first sed is that it would make a mess of a line that contains two sets of tags with attributes. – Kusalananda Apr 8 '18 at 19:17

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.