0

I have a XML file like this (A.xml) :

<?xml version="1.0"?>
<RunParameters xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <RunParametersVersion>NextSeq_4_0_0</RunParametersVersion>
  <ReagentKitSerialWasEnteredInBaseSpace>false</ReagentKitSerialWasEnteredInBaseSpace>
  <ExperimentName>210913-RUN61-COCO</ExperimentName>
  <PurgeConsumables>false</PurgeConsumables>
  <MaxCyclesSupportedByReagentKit>92</MaxCyclesSupportedByReagentKit>
  <ModuleName />
  <ModuleVersion />
</RunParameters>

And I want to set a bash variable that contains the RUN61 port of the XML tag <ExperimentName>210913-RUN61-COCO</ExperimentName>. The tag value always has the structure

irrelevant-relevant-irrelevant

separated by dashes.

I tried with grep but whitout any good result:

runNumber=$(grep -o '<ExperimentName>.*</ExperimentName>' | cut -d '-' -f2 A.xml)

Do you know how to do it?

5
  • 1
    usually, I'd ask what "whitout any good result" means, exactly. That is, what result you get instead, looking at that usually helps in figuring out what happens. But here, it's enough to look at the second part of the pipeline. What does cut -d '-' -f2 A.xml do?
    – ilkkachu
    Sep 14 at 9:21
  • How is the string inside the <ExperimentName> tags formatted. It it always irrelevant-relevant-irrelevant? Is <ExperimentName> always under <RunParameters>? Can there be more than one <ExperimentName>?
    – AdminBee
    Sep 14 at 9:23
  • @AdminBee: With due respect, I don't see how bash tag is relevant to the question here. It is standard text processing stuff. If its a shell script, the tags should be shell-script. The question is basically shell agnostic
    – Inian
    Sep 14 at 9:29
  • @AdminBee Yes, exactly . And always with the structure XX-YY-ZZ
    – nstatam
    Sep 14 at 9:30
  • @Inian The OP states that the operation is part of a shell script, so the (subordinate) task is to import the text processing result into a shell variable. Also, although not recommended, someone may want to add a solution that purely relies on shell text processing methods, so the shell syntax to adhere to may be (and you are right, only "may be", not "is absolutely") relevant. If you disagree, feel free to remove the tag...
    – AdminBee
    Sep 14 at 9:32
3

Since you are processing structured data, you should use a dedicated parser such as xmlstarlet to extract the tag value for feeding to cut:

xmlstarlet sel -t -c "string(/RunParameters/ExperimentName)" A.xml | cut -d- -f 2

So, you can use

runNumber=$(xmlstarlet sel -t -c "string(/RunParameters/ExperimentName)" A.xml | cut -d- -f 2)
1

Using only xmlstarlet:

experiment_name=$(
    xmlstarlet sel -t \
        -m '/RunParameters/ExperimentName' \
        -v 'substring-before(substring-after(., "-"), "-")' file.xml
)

This matches the node that we're interested in, and then strips out the middle part of that node's value using the two functions substring-after() and subsring-before().

The output of xmlstarlet is then assigned to the variable experiment_name.

Alternatively, using xq from https://kislyuk.github.io/yq/

experiment_name=$(
    xq -r '.RunParameters.ExperimentName | split("-")[1]' file.xml
)

This simply splits the node's value on the dashes and returns the second element in the resulting array.

0

Using Raku (formerly known as Perl_6)

raku -MXML -e 'for open-xml($*ARGFILES) {.elements(:TAG<ExperimentName>)>>.contents.put};' < input.xml

Sample Input:

<?xml version="1.0"?>
<RunParameters xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <RunParametersVersion>NextSeq_4_0_0</RunParametersVersion>
  <ReagentKitSerialWasEnteredInBaseSpace>false</ReagentKitSerialWasEnteredInBaseSpace>
  <ExperimentName>210913-RUN61-COCO</ExperimentName>
  <PurgeConsumables>false</PurgeConsumables>
  <MaxCyclesSupportedByReagentKit>92</MaxCyclesSupportedByReagentKit>
  <ModuleName />
  <ModuleVersion />
</RunParameters>

Sample Output:

210913-RUN61-COCO

As others have mentioned, you will most certainly want to use a dedicated XML parser for this task. Briefly, for the above code Raku is called at the bash command line, and -M module XML is loaded with the command -MXML. Note the above code relies on < shell redirection [without redirection, you must stringify open-xml() input to $*ARGFILES.Str ]. The xml file is opened with open-xml and queried for desired TAG, contents are extracted and returned with put.

Actually, the OP provided perfectly good code using cut to extract the RUN61 portion of the output, and the above Raku solution can be simply piped through the OP's code. However, for an all-Raku solution, simply interpose a call to .split("-")[1] between .contains and .put in the Raku code above:

raku -MXML -e 'for open-xml($*ARGFILES.Str) {.elements(:TAG<ExperimentName>)>>.contents.split("-")[1].put};'

https://github.com/raku-community-modules/XML
https://www.raku.org

-1

You can also extract that name by grep (option -E allows extended regular expressions):

runNumber=$(grep -Eo '[[:alnum:]]+-[[:alnum:]]+' A.xml | cut -d- -f2)

If you want to ensure the line of that tag, you can pre-filter it by another grep command:

runNumber=$(
  grep '<ExperimentName>' A.xml \
  | grep -Eo '[[:alnum:]]+-[[:alnum:]]+' \
  | cut -d- -f2
)

NOTE:

Solutions based on XPath-expressions:

  • are more readable
  • might be much more fault tolerant
  • but they can introduces some extra dependencies
1
  • -1 means "This answer is not useful". Ok, thank you for the feedback from somebody. Why is this answer not useful? Can anyone explain? "-1" is just a number, a simple dislike without any useful details. I cannot learn from that.
    – benaja
    Sep 18 at 8:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.