Using XMLStarlet, this would look something like the following:
#!/bin/bash
# usage: [script] [flatfile-name] <in.xml >out.xml
flatfile=$1
# store an array of variables, and an array of edit commands
xml_vars=( )
xml_cmd=( )
count=0
while read -r line; do
[[ $line = *"~"* ]] || continue
key=${line%%"~"*} # put everything before the ~ into key
key=${key//"."/"/"} # change "."s to "/"s in key
val=${line#*"~"} # put everything after the ~ into val
# assign key to an XMLStarlet variable to avoid practices that can lead to injection
xml_vars+=( --var "var$count" "'$key'" )
# update the first value following a matching name
xml_cmd+=( -u "//name[.=\$var${count}]/following-sibling::value[1]" \
-v "$val" )
# increment the counter used to assign variable names
(( ++count ))
done <"$flatfile"
if (( ${#xml_cmd[@]} )); then
xmlstarlet ed "${xml_vars[@]}" "${xml_cmd[@]}"
else
cat # no edits to do
fi
This will run a command like the following:
xmlstarlet ed \
--var var0 "Application/Env" \
--var var2 "Application/ID" \
--var var3 "Application/Name" \
-u '//name[.=$var0]/following-sibling::value[1]' -v 'DEV' \
-u '//name[.=$var1]/following-sibling::value[1]' -v '999' \
-u '//name[.=$var2]/following-sibling::value[1]' -v 'appname'
...which replaces the first value after the name Application/Env
with DEV
, the first value after the name Application/ID
with 999
, and the first value after the name Application/Name
with appname
.
A slightly less paranoid approach might instead generate queries like //name[.="Application/Name"]/following-sibling::value[1]
; putting the variables out-of-band is being followed as a security practice. Consider what could happen otherwise if the input file contained:
Application.Foo"or 1=1 or .="~bar
...and the resulting XPath were
//name[.="Application/Foo" or 1=1 or .=""]/following-sibling::value[1]
Because 1=1
is always true, this would then match every name, and thus change every value in the file to bar
.
Unfortunately, the implementation of XMLStarlet doesn't effectively guard against this; however, using bind variables makes it possible for an implementation to provide such precautions, so a future release could be safe in this context.