This problem consists of two sub-problems:
Finding the maximum
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
Updating the attribute
Constant value for practice
cat jumps.xml | xmlstarlet edit --update //pupil/@highestJump -v "Hahahaha"
...writes Hahahaha to every highestJump attribute.
Simple XPath
Take care: The XPath you use for replacing
- is relative to the selected attribute (so
. is the attribute itself)
- has to be wrapped with eg.
string() to have an effect
So:
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">
Combining the two
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>