Update specific value in xml file by bash - linux

I need update specific value in xml in automatic way by bash script.
My xml file has a lot of similar line like:
<xml>
<main>
<buildElement name="test_one" version="" path="" />
<buildElement name="test_two" version="" path="" />
</main>
</xml>
I need find element name "test_one" and edit version.
I am trying this, but it's not help:
Expected output:
<xml>
<main>
<buildElement name="test_one" version="some_value" path="" />
<buildElement name="test_two" version="" path="" />
</main>
</xml>
I am trying get this by xmlstarlet and sed, but is not working f.e:
xmlstarlet edit --update '//xml/main/buildElement/name="test_one"/version' --value 'some_value' myXML.xml

Your xpath syntax is incorrect. You need to use # to refer to attributes, and to search for a particular element you need a filter expression. You want:
xmlstarlet edit --update \
'//xml/main/buildElement[#name="test_one"]/#version' \
-v some_value myXML.xml
Which will output:
<?xml version="1.0"?>
<xml>
<main>
<buildElement name="test_one" version="some_value" path=""/>
<buildElement name="test_two" version="" path=""/>
</main>
</xml>

'//xml/main/buildElement/name="test_one"/version'
You want
'//xml/main/buildElement[#name="test_one"]/#version'
(Basically, you seem to be guessing, and that's not going to get you very far with XPath. Do some reading.)

Related

Reading source files in MSVS project using xmlstarlet

I am trying to get the source files and include directories from a vcxproj file. Eg of vcsproj:
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
....
<ItemGroup>
<ClCompile Include="$(SrcDir)d1\f1cpp" />
<ClCompile Include="$(SrcDir)d2\f2.cpp" />
</ItemGroup>
...
</Project>
I tried this:
xmlstarlet sel -t -v "//_:ItemGroup/ClCompile/#Include" myProj.vcxproj but didn't work.
However, when I tried this (copying the code from some page that I came across), it works:
echo '<?xml version="1.0" encoding="utf-8"?> <ELEMENT xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<SUB_ELEMENT attribute="attr_value"/>
</ELEMENT>' | xmlstarlet sel -t -v '//_:SUB_ELEMENT/#attribute' --nl
o/p: attr_value
I don't see how the two are different with respect to reading an attribute value from an xml with a namespace. I further tried a stripped down version of the vcxproj file:
echo '<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="$(SrcDir)d1\f1cpp" /> </ItemGroup> </Project>' | xmlstarlet sel -t -v '//_:ItemGroup/#Include' --nl
o/p: <no o/p>
Any indication on why it is not working or how to get this to work would be very helpful.
Edit: Expected output from the vcxproj would be a list of filenames. For the above command it would be $(SrcDir)d1\f1cpp
Any indication on why it is not working or how to get this to work would be very helpful
Since you're using the default namespace add the _:
shortcut on the node test in each
location step,
and the -T (--text)
option to make text mode output:
xmlstarlet select -T -t -v "//_:ItemGroup/_:ClCompile/#Include" -n file.xml

AWK to replace HTML tag with another and keep text

I am looking for a way to replace a HTML tag with another, but keep the text.
I have a big HTML file, which contains:
<span class="desc e-font-family-cond">fork</span>
I want to replace <span> tag with <strong> tag:
<strong>fork</strong>
Tool doesn't really matter, but I am looking for a CLI way to do it.
I am not looking for a HTML processor, because input is a text file with some HTML code in it (not a clean/valid HTML) and I am manually working with the output (copy, modify, use later in its final place). I just want to save some time with the replace.
I would use GNU sed for this task following way, let file.txt content be
<span class="desc e-font-family-cond">fork</span>
then
sed -e 's/<span[^>]*>/<strong>/g' -e 's/<\/span>/<\/strong>/g' file.txt
output
<strong>fork</strong>
Explanation: firstly replace span starting using <strong>, secondly replace span closing using </strong>.
Consider using Python and a tool like BeautifulSoup to handle HTML. Trying to parse HTML with other tools like sed or awk can lead to terrible places.
As an example:
from bs4 import BeautifulSoup
soup = BeautifulSoup('<li><span class="desc e-font-family-cond">fork</span>')
for spanele in soup.findAll('span'):
spanele.name = 'p'
html_string = str(soup)
print(html_string);
That's lightweight and pretty simple and the html is handled properly with a library that is specifically built to parse it.
Don't use AWK for processing HTML files. If you can turn your HTML file into an XHTML file, you can use xsltproc for an XML transformation as follows:
trans.xsl file:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" encoding="utf-8"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="span[#class='desc e-font-family-cond']">
<strong><xsl:apply-templates/></strong>
</xsl:template>
</xsl:stylesheet>
CLI command for invoking xsltproc, which has to be installed, obviously:
xsltproc trans.xsl file.html
The standard output of this command is the corrected HTML file as you want to have it.
Using sed:
sed 's,<\(\/\)\?span\(\s\)\?,<\1strong\2,g'
$ echo '<span class="desc e-font-family-cond">fork</span>' | sed 's,<\(\/\)\?span\(\s\)\?,<\1strong\2,g'
<strong class="desc e-font-family-cond">fork</strong>

AsciiDoctor: How can I add custom xmlns'

How can I add a custom xmlns in the output when I convert an asciidoc file with AsciiDoctor?
I'd like to add xmlns:xi="http://www.w3.org/2001/XInclude" in the top book tag.
The current implementation seems to generate:
<?xml version="1.0" encoding="UTF-8"?>
<?asciidoc-toc?>
<?asciidoc-numbered?>
<book xmlns="http://docbook.org/ns/docbook" xmlns:xl="http://www.w3.org/1999/xlink" version="5.0" xml:lang="en">
<info>
<title>title</title>
</info>
</book>
from this:
= title
:lang: en
When I run:
$ asciidoctor -b docbook5 -d book -o out.xml source.txt
There is a built-in attribute xmlns, but it seems to be for docbook 4.5.
The reason I want to use XInclude is to include some xml files from Docinfo files and Passthrough Blocks
With a bit of research inside the asciidoctor code it quickly became clear that the part you'd like to modify is fairly static.
See asciidoctor/converter/docbook5.rb Line 44 for more info.
The best approach is to create a postprocessor extension which modifies the output. The example below is just to show a possible implementation.
Create a file with the following content and call it docbook_postprocessor.rb.
class Docbook5XiPostprocessor < Asciidoctor::Extensions::Postprocessor
def process document, output
if document.basebackend? 'docbook'
input_regex = %r{^(<.*xmlns:xl="http://www.w3.org/1999/xlink") (version="5.0".*>)}
replacement = %(\\1 xmlns:xi="http://www.w3.org/2001/XInclude" \\2)
output = output.sub(input_regex, replacement)
end
output
end
end
Asciidoctor::Extensions.register do
postprocessor Docbook5XiPostprocessor
end
Note: The above extension is for the sake of brevity placed in the same directory as the asciidoctor source file called source.adoc.
The run the asciidoctor command with the -r ./docbook_postprocessor.rb parameters.
$ asciidoctor -r ./docbook_postprocessor.rb -b docbook5 -d book -o - source.adoc
<?xml version="1.0" encoding="UTF-8"?>
<?asciidoc-toc?>
<?asciidoc-numbered?>
<book
xmlns="http://docbook.org/ns/docbook"
xmlns:xl="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
version="5.0"
xml:lang="en">
<info>
<title>test</title>
<date>2020-12-19</date>
</info>
</book>
* Above output has been slightly reformatted to eliminate the scrollbar
Creating ruby gem with the above code for easier distribution is a task left to the reader.

shell script sed replace

I have this file config.xml
<widget id="com.example.hello" version="0.0.1">
<name>HelloWorld</name>
<description>
A sample Apache Cordova application that responds to the deviceready event.
</description>
<author email="dev#callback.apache.org" href="http://cordova.io">
Apache Cordova Team
</author>
<enter>PASSWORD</enter>
<content src="index.html" />
<access origin="*" />
I tried to do it with sed without success.
I need to do this:
$./script.sh config.xml NEWPASSWORD
to get:
<widget id="com.example.hello" version="0.0.1">
<name>HelloWorld</name>
<description>
A sample Apache Cordova application that responds to the deviceready event.
</description>
<author email="dev#callback.apache.org" href="http://cordova.io">
Apache Cordova Team
</author>
<enter>NEWPASSWORD</enter>
<content src="index.html" />
<access origin="*" />
Using backreference:
sed "s/^\( *<enter>\)\([^>]*\)</\1$2</" "$1"
^\( *<enter>\): search for lines starting with any number of spaces followed by <enter>. Matching characters are captured with escaped parentheses.
\([^>]*\)<: following characters up top next < are captured in a second group.
\1$2<: in the substitution string, characters from first group are output(\1) followed by the second parameter value passed to the script, ($2, the new password value)
The command is applied to $1, the file passed as first parameter to the script (the file name).
To edit the file in place, use the -i flag:
sed -i "s/^\( *<enter>\)\([^>]*\)</\1$2</" "$1"
The good result is:
$cat script.sh
#!/bin/sh
file=$1
sed -i "s/^\( *<enter>\)\([^>]*\)</\1$2</" "$1"
Then:
$./script.sh config.xml NEWPASSWORD
Many thanks to everyone, especially to Kenavoz.

Select the string of an XSLT param given at command line

I am using XSLT params to set an absolute path in an attribute at runtime using Xalan-C. Basically, my input XML is something like this :-
<root xmlns="initial">
<!-- document goes here -->
</root>
My stylesheet is :-
<xsl:stylesheet version="1.0" xmlns:s="initial" xmlns="final" />
<xsl:param name="default_data_location">/path/to/some/location</xsl:param>
<xsl:template match="//s:*">
<xsl:element name="{local-name()}" namespace="final">
<xsl:attribute name="dataLocation">
<xsl:value-of select="concat($default_data_location, '/datafile')"/>
</xsl:attribute>
</xsl:element>
</xsl:template>
<!-- rest of the stylesheet -->
</xsl:stylesheet>
Thus, my desired output XML when I run it as :-
Xalan foo.xml foo.xsl
should be (this is the part that works) :-
<root xmlns="final" dataLocation="/path/to/some/location/datafile">
<!-- document goes here -->
</root>
And when I run it as :-
Xalan -p default_data_location /some/other/path foo.xml foo.xsl
it should be (and this is the part that doesn't work) :-
<root xmlns="final" dataLocation="/some/other/path/datafile">
<!-- document goes here -->
</root>
If I try to set this param at the command line, however, it gives me the following XML :-
<root xmlns="final" dataLocation="/datafile">
<!-- document goes here -->
</root>
What should I be doing?
The parameter value seems to be an XPath expression so you need to make sure you pass in an XPath string and you might need to double quotes to make sure the command line shell does not get into your way so doing Xalan -p default_data_location "'/some/other/path'" foo.xml foo.xsl should work. At least that's my reading of the documentation at http://xml.apache.org/xalan-c/commandline.html, I don't have Xalan-C to test.

Resources