Consider the following XML:
<?xml version="1.0"?>
<sportsClass>
<pupils>
<pupil name="Adam" highestJump="">
<jump height="4"/>
<jump height="1"/>
</pupil>
<pupil name="Berta" highestJump="">
<jump height="4"/>
<jump height="7"/>
</pupil>
<pupil name="Caesar" highestJump="">
<jump height="1"/>
<jump height="2"/>
</pupil>
<pupil name="Doris" highestJump="">
<jump height="5"/>
<jump height="5"/>
</pupil>
</pupils>
</sportsClass>
How can I fill the highestJump
attribute nodes with the respective maximum height
value, using xmlstarlet?
This problem consists of two sub-problems:
xmlstarlet does not have the max()
function, so we have to find a way around:
cat jumps.xml | \
xmlstarlet select -t -v "//pupil/jump[not(@height <= following-sibling::jump/@height) and not(@height < preceding-sibling::jump/@height)]/@height"
Note the <=
and <
– if there are more than one maximum values, only the last one will be taken.
Result:
4
7
2
5
cat jumps.xml | xmlstarlet edit --update //pupil/@highestJump -v "Hahahaha"
...writes Hahahaha
to every highestJump
attribute.
Take care: The XPath you use for replacing
.
is the attribute itself)string()
to have an effectSo:
cat jumps.xml | xmlstarlet edit --update //pupil/@highestJump -x "string(../@name)"
...gives (shortened):
<pupil name="Adam" highestJump="Adam">
<pupil name="Berta" highestJump="Berta">
<pupil name="Caesar" highestJump="Caesar">
<pupil name="Doris" highestJump="Doris">
cat jumps.xml | xmlstarlet edit --update //pupil/@highestJump -x "string(../jump[not(@height <= following-sibling::jump/@height) and not(@height < preceding-sibling::jump/@height)]/@height)"
...gives...
<?xml version="1.0"?>
<sportsClass>
<pupils>
<pupil name="Adam" highestJump="4">
<jump height="4"/>
<jump height="1"/>
</pupil>
<pupil name="Berta" highestJump="7">
<jump height="4"/>
<jump height="7"/>
</pupil>
<pupil name="Caesar" highestJump="2">
<jump height="1"/>
<jump height="2"/>
</pupil>
<pupil name="Doris" highestJump="5">
<jump height="5"/>
<jump height="5"/>
</pupil>
</pupils>
</sportsClass>