问题
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
value
fromid
inX.xml
is different thanvalue
fromkey
inY.properties
, thevalue
fromkey
inY.properties
should be updated. - If
value
fromid
inX.xml
is the same asvalue
fromkey
inY.properties
, nothing should be done - If
id
fromX.xml
is not present askey
inY.properties
, it should be added with itsvalue
fromX.xml
toY.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.properties
as 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 thexmlstarlet
output 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