问题
The following xmlstarlet command:
./xmlstarlet-1.5.0/xml.exe fo --dropdtd $filename | ./xmlstarlet-1.5.0/xml.exe sel -t -m "//DOC//PTXT" -v "concat(./@ID,' ', .)"
returns multiple results, because my file contains a lot of doc/ptxt. I need to do something with each output, more explicitly I need to do something with each ptxt value. How can I loop through all results of XMLstarlet?
My output looks something like:
my.id.one Text I need
my.id.two Text I also need
my.id.three Surprisingly I need this text too
and what I need is to have each id-text pair in maybe two variables id and text, because I have another file containing id-value pairs and I need to match those pairs.
Update 1:
A concrete example of what I need to do:
I have file X.xml and file Y.properties
File X.xml has the following structure:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE DOC SYSTEM "ts.dtd">
<?xml-stylesheet type="text/css" href="ts.css"?>
<DOC LOCALE="en-US">
<PTXT ID="text.something">Open door</PTXT>
<PTXT ID="text.something.else">Open another door</PTXT>
<PTXT ID="text.whatever">Close all</PTXT>
</DOC>
File Y.properties has the following structure:
text.something=Open window
text.something.else=Open another door
The result that I expect is Y.properties with this content:
text.something=Open door
text.something.else=Open another door
text.whatever=Close all
To do:
- If
valuefromidinX.xmlis different thanvaluefromkeyinY.properties, thevaluefromkeyinY.propertiesshould be updated. - If
valuefromidinX.xmlis the same asvaluefromkeyinY.properties, nothing should be done - If
idfromX.xmlis not present askeyinY.properties, it should be added with itsvaluefromX.xmltoY.properties
My current shell code is:
for filename in C:/Temp/XLocation/*.xml; do
./xmlstarlet-1.5.0/xml.exe fo --dropdtd $filename | ./xmlstarlet-1.5.0/xml.exe sel -t -m "//DOC//PTXT" -v "concat(./@ID,' ', .)"
done
which currently only takes every id/value from X.xml and prints to std output.
The for is there, as you probably suspect, because I have multiple X.xml and Y.properties on which I must execute this code.
Problem 1: @janos I have the following problem and I really don't understand why. It all works ok, but few of them don't. Example: X.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE DOC SYSTEM "ts.dtd">
<?xml-stylesheet type="text/css" href="ts.css"?>
<DOC>
<PTXT ID="a.b.c.d" CONTEXT="label"><NTI>Text</NTI></PTXT>
</DOC>
Y.properties:
a.b.c.d=Text
and my output is:
a.b.c.d=
Text=
=
Can you please help me as I really don't understand what's going on.
Problem 2: Having the following input: X.xml
my.id = \u00D6ffnen Express WebTools
and Y.properties
<PTXT ID="my.id" CONTEXT="">Öffnen <NTI>Express WebTools</NTI></PTXT>
results in: out.properties
my.id=Öffnen Express WebTools
my.id=\u00D6ffnen Express WebTools
instead of
my.id=Öffnen Express WebTools
回答1:
First generate the data to match the format in Y.properties:
xmlstarlet fo --dropdtd a.xml | \
xmlstarlet sel -t -m "//DOC//PTXT" -v $'concat(@ID, "=", ., "\n")'
For your example this produces:
text.something=Open door
text.something.else=Open another door
text.whatever=Close all
Then you can use Awk to perform the following logic:
- Use the output of the previous command as the first file with
- - Use
Y.propertiesas the second file - Process line by line, using
=as a separator, and building up a map of key-value pairs, using a simple logic: if the key is not in the map, add the key-value pair. Since we will get the lines of thexmlstarletoutput first, the above logic will add all those values to the mapping. As we process the lines of the second file (Y.properties), lines whose key is already in the map will be ignored, but new key-value pairs will be added.
Like this:
xmlstarlet fo --dropdtd a.xml | \
xmlstarlet sel -t -m "//DOC//PTXT" -v $'concat(@ID, "=", ., "\n")' | \
awk -F= '!($1 in m) { m[$1] = $2 }
END { for (key in m) { print key "=" m[key] } }' - Y.properties
You can redirect the output to the desired target file.
To perform the above to multiple files, you can wrap the above code in a function, with appropriate parameters. For example:
mergeprops() {
local filename=$1
local propsfile=$2
local out=$3
xmlstarlet fo --dropdtd "$filename" | \
xmlstarlet sel -t -m "//DOC//PTXT" -v $'concat(@ID, "=", ., "\n")' | \
awk -F= '!($1 in m) { m[$1] = $2 }
END { for (key in m) { print key "=" m[key] } }' - "$propsfile" | sed 's/\s*=\s*/=/g' > "$out"
}
for filename in /c/Temp/XLocation/*.xml; do
mergeprops "$filename" Y.properties "$filename.out"
done
Let me know if you need more help.
来源:https://stackoverflow.com/questions/47411993/update-properties-file-with-values-from-xml-file