2

I need to replace, in an XML file, specific strings of characters in some specified tags with other strings of characters embedded in tags.

Example searching every occurance of an example, that needs to be replaced by <b>a test</b> only if it is within the <a> ... </a> tag (inside other tags):

  • Input example:
    <c>This is an example. <a>This is an example;</a></c>
    
  • Desired output:
    <c>This is an example. <a>This is <b>a test;</b></a></c>
    
7
  • This seems to be unrelated to linux / unix and a general XSLT problem – > might be much, much better off at Stackoverflow.ocm than here! Commented Apr 24, 2022 at 11:39
  • 1
    @MarcusMüller I would assume instead that it's about how to make the appropriate tools perform the necessary change. Here, for example, the <a>This is *an example*</a> needs to be changed to <a>This is </a> with an embedded element <d>a test</d>. The XSLT came from fpmurphy's answer to the cross-referenced question. Commented Apr 24, 2022 at 12:14
  • 1
    Indeed, I would like to generalise the answer given in this forum, so I believe it is the correct place to ask it. More specifically, I do not know what kind of wildcard I need to use to search for just a group of specified characters, and not the whole chain of characters within a tag. In addition, I do not know how to deal with the XML tags in the replacing group of characters. I tried using xmlstarlet as presented here: unix.stackexchange.com/questions/608341/…, but ran into the issue, a.o., of the special characters "<" and ">".
    – Matt5
    Commented Apr 24, 2022 at 13:23
  • 1
    I only wish to add formatting tags in an XML file that is a DB export (and that is partly tagged), but these additional tags should only be placed to specific chains of characters that are embedded in specific tags. The XML file is then imported in InDesign. So it is not about XML transformation.
    – Matt5
    Commented Apr 24, 2022 at 13:57
  • 1
    I removed the XSL part, as I believe this is misleading. I just need to do a complex/conditional search and replace within tags, either using SED, GREP, xmlstarlet or any other unix command line. From what I have read, the use of XSL was one possible way to do that.
    – Matt5
    Commented Apr 24, 2022 at 14:40

1 Answer 1

2

It seems like you want to

  1. remove the text an example; from the value of the /c/a node in the XML document, and
  2. add a sub-node to the /c/a node called b, with the value a test;.

You can easily do this with xmlstarlet in the shell:

xmlstarlet ed -u '/c/a' -x 'substring-before(text(), "an example;")' file.xml |
xmlstarlet ed -s '/c/a' -t elem -n 'b' -v 'a test;'

The first invocation of xmlstarlet on the example document in the question would result in the following output, where some text is removed from the /c/a node's value:

<?xml version="1.0"?>
<c>This is an example. <a>This is </a></c>

The second invocation takes this modified document and produces the following by introducing the /c/a/b node:

<?xml version="1.0"?>
<c>This is an example. <a>This is <b>a test;</b></a></c>

The xmlstarlet invocations may be combined into a single command. Below, I've used the long options and also use --inplace for in-place editing of the original document (this is for illustration only, you should run without --inplace to determine the transformations works first):

xmlstarlet ed --inplace \
    --update  '/c/a' -x 'substring-before(text(), "an example;")' \
    --subnode '/c/a' -t elem -n 'b' -v 'a test;' file.xml

A generalisation of the above to something that performs the two edits to any a node containing the text an example; (which is what was actually requested in the question):

xmlstarlet ed \
    --var paths '//a[contains(text(), "an example;")]'  \
    --update  '$paths' -x 'substring-before(text(), "an example;")' \
    --subnode '$paths' -t elem -n 'b' -v 'a test;' file.xml 

The only new thing here is that we first store the paths for all the nodes we want to edit in the internal variable $paths. We then refer to these paths in the --update and --subnode modifications.

5
  • Thank you so much for your answer. I am now trying on my file, with the exact search and replace I need: xmlstarlet ed --var paths ‘//CS_CT[contains(text(), “། །”)]’ --update '$paths' -x 'substring-before(text(), “། །“)’ --subnode '$paths' -t elem -n ‘CS_NBSS’ -v ‘། །’ file_mod.xml But I have that shell error message: -bash: syntax error near unexpected token `(' I've checked carefully and believe I followed your syntax. Do you see anything wrong? I've tried with CSCT and CSNBSS, and replacing the Tibetan characters with letters, but still the error is the same
    – Matt5
    Commented May 5, 2022 at 8:37
  • 1
    @Matt5 Your code in the comment does not use straight quotes (" and '), but typographical ("fancy") quotes as written by some word processors ( and ). This is going to cause issues in the shell.
    – Kusalananda
    Commented May 5, 2022 at 8:39
  • It works! Thank you so much!
    – Matt5
    Commented May 6, 2022 at 9:23
  • Sorry... I would like to know what would be your answer for an example searching every occurance of an example, that needs to be replaced by <b>a test</b> only if it is NOT within the <a> ... </a> tag (inside other tags): * Input example: xml <c>This is an example. <a>This is an example;</a></c> * Desired output: xml <c>This is <b>a test.</b> <a>This is an example;</a></c>
    – Matt5
    Commented May 6, 2022 at 10:07
  • I have asked a related question in unix.stackexchange.com/q/701641/523366.
    – Matt5
    Commented May 6, 2022 at 12:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .