I am using xmlstarlet to extract text from elements with a specific preceding sibling event. An example from the XML file:

 <event type='cue' units='sec'>
    <value name='side'>CUER</value>
  <event type='target' units='sec'>
    <value name='side'>TARGETR</value>
    <value name='RT' units='msec'>379</value>
    <value name='TargCorr'>1</value>
    <value name='feedback'>YOU WIN!</value>
  <event type='anticipation' units='sec'>
    <value name='TargCorr'>1</value>
    <value name='feedback'>YOU WIN!</value>

From the example, I need to do the following:

  1. print the onset of <event type='target', and
  2. print the sum of the duration of the <event type='target' and immediately following duration of the <event type='anticipation'.

I can print the correct onset using the "preceding-sibling" option:

xmlstarlet sel -t \
 -m '//event[@type="anticipation" and value[@name="feedback"]="YOU WIN!"]' \
 -m 'preceding-sibling::event[@type="target" and value[@name="feedback"]="YOU WIN!"][1] ' \
 -v 'onset' -o ' ' -v 'duration' -o ' ' -o '1' -n $DIR/$xml \
   > $DIR/output.stf

Though as written, the following code only displays the duration from the matched element rather than summing the duration of the two adjacent events. Is the latter possible using xmlstarlet?

Thank you for your help!

  • 3
    +1 for having even attempted to process XML with an XML parser. You're a step ahead of most Commented Feb 14, 2023 at 22:27

1 Answer 1


I'm answering without looking too closely at the code that you provide since it does not correspond with the description that precedes it.

Assuming you want to retrieve the onset value for each event node with a type attribute with the value target, you can do that using xmlstarlet with

xmlstarlet select --template \
    --value-of '//event[@type = "target"]/onset' \
    -nl file

Another way to write that is by matching the wanted set of nodes and extracting the onset value from each in turn. This, in effect, introduces a way of doing multiple operations on a single node before moving on to the next matched node.

xmlstarlet select --template \
    --match '//event[@type = "target"]' \
    --value-of 'onset' \
    -nl file

The "following sibling node" is had with the following-sibling selector, and we get the first of the subsequent event siblings with a type attribute with the value anticipation using following-sibling::event[@type = "anticipation"][1].

Adding up the duration values of the matched node and its sibling node is done with sum(), and we combine that with the command above like so:

xmlstarlet select --template \
    --match '//event[@type = "target"]' \
    --value-of 'onset' --output ' ' \
    --value-of 'sum(duration | following-sibling::event[@type = "anticipation"][1]/duration)' \
    -nl file 

Or, using short options,

xmlstarlet select -t \
    -m '//event[@type = "target"]' \
    -v 'onset' -o ' ' \
    -v 'sum(duration | following-sibling::event[@type = "anticipation"][1]/duration)' \
    -n file 

Note the syntax for creating a node-set for the sum() function. The set of nodes that will be summed matches either of the |-delimited XPath queries.

Given the example data in the question, with some encapsulating root node added, this would output

11.367 3.91

... where the first number is the onset number from the target node, and the second is the sum of the duration values of the target node and the associated sibling anticipation node.

  • Done! Quick addition to the above answer: I added a [1] after following-sibling::event[@type = "anticipation"] to limit the depth as it was summing all following anticipation nodes in the full XML file. Thanks again!
    – XMLnewbie
    Commented Feb 15, 2023 at 16:58
  • @XMLnewbie Ah, that's a good addition. I only had your short example document to test with, so I didn't catch that issue. I will update (EDIT: have updated) my answer with this. Thanks!
    – Kusalananda
    Commented Feb 15, 2023 at 17:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .