0

I have this command:

find . -iname '*.xml' | xargs xmlstarlet sel -N z="http://abc.com/article/1.0/" \\
--var zgtag="SuperTag" -t -m "/z:profile-extension" -i "//z:tag='$zgtag'" \\
-n -f -o "," -v "count(//z:zgContextsAnonymTotal[z:tag='$zgtag'])" -o "," \\
-v "//z:zgContextsAnonymTotal/z:tag[.='$zgtag']/@z:timestamp" -o "," \\ 
-v  "count(//z:zgContextsREGTotal[z:tag='$zgtag'])" -o "," \\
-v "//z:zgContextsREGTotal/z:tag[.='$zgtag']/@z:timestamp" -o "," \\
-v "count(//z:zgContextsPremiumTotal[z:tag='$zgtag'])" -o "," \\
-v "//z:zgContextsPremiumTotal/z:tag[.='$zgtag']/@z:timestamp" | grep -v ^$

It does not work because the command version without variable (same value repeated instead of using $zgtag show results. What is wrong?

My investigation so far pointed out:

  • It has to be --var zgtag="'SuperTag'" because without single quotes the compiled XSLT by xmlstarlet would produce the following variable declaration: <xsl:variable select="SuperTag" name="zgtag"/> which is obviously wrong, the single quotes are missing!
  • In the XSLT compiled by xmlstarlet I can see that the variable is NOT inserted correctely, e.g. I see this:
<xsl:choose>
  <xsl:when test="//z:tag=''">

Then I used this version: --var zgtag="'SuperTag'" -m "/z:profile-extension" -i '//z:tag=$zgtag', now I get a correctly compiled XSLT debugging output of xmlstarlet:

  <xsl:template match="/">
    <xsl:variable select="'SuperTag'" name="zgtag"/>
    <xsl:for-each select="/z:profile-extension">
      <xsl:choose>
        <xsl:when test="//z:tag=$zgtag">

However the output still is empty, the same script without the variable (SuperTag directly inserted into the script multiple times) works just fine. So there must be still a problem with this var-styled command.

So any help welcome how to insert and use the defined variable correctly. The definition is not the problem, it's the usage of the variable in the xmlstarlet command.

basZero
  • 153
  • 2
    Can you demonstrate the problem with a simplified command? What happens? What do you want to happen? – Chris Davies Feb 07 '24 at 09:59
  • Could you try with --var zgtag="'SuperTag'" and also remove the single quotes in the expression around $zgtag? – Kusalananda Feb 07 '24 at 10:00
  • Is the issue that you're trying to use a shell variable inside the XML XPath? It's hard to tell without an example – Chris Davies Feb 07 '24 at 10:04
  • Oh, yes, what Chris said. You seem to be using double quotes around the expressions. You should be using single quotes, or escape the $. I didn't spot that due to the amount of text. – Kusalananda Feb 07 '24 at 10:07
  • @Kusalananda I tried this as you said: --var zgtag="'fbfBfzgartAudiquattro'" -t -m "/z:profile-extension" -i "//z:tag=$zgtag", i get the error could not compile test expression '//z:tag=' – basZero Feb 07 '24 at 14:50
  • i think it has to do with the scope of the variable. It seems that it is just empty when it is used. – basZero Feb 07 '24 at 14:54
  • I added more info of my investigation into the question. – basZero Feb 07 '24 at 15:06
  • 1
    @basZero You still use double quotes around the expression. You can't do that without escaping the $ so that the shell does not think that $zgtag is a shell variable. – Kusalananda Feb 07 '24 at 15:54
  • @Kusalananda have you seen my additions to my question at the end of it? The current version looks ok from a XSLT point of view, xmlstarlet generates correctly this: <xsl:when test="//z:tag=$zgtag"> , but as I said, it still does not work. – basZero Feb 08 '24 at 07:34
  • It's difficult to debug and test without data, but I'm also no expert in XML parsing in general, so I will unlikely be of much help to you. – Kusalananda Feb 08 '24 at 07:53

1 Answers1

1

Let's take a simplified part of your expression (without the namespace, etc.)

xmlstarlet sel --var zgtag="SuperTag" -t -m "/profile-extension" -i "//tag='$zgtag'"

You've declared an XPath variable zgtag, referenced as $zgtag. However, you've put the expression //tag='$zgtag' inside double-quotes, so the shell can parse it. In doing so it evaluates variables, and it knows that as far as it is concerned, $zgtag is unset. So you get this:

xmlstarlet sel --var zgtag="SuperTag" -t -m "/profile-extension" -i "//tag=''"

The command is then executed - and obviously it doesn't do what you want.

The solution, as ever, is to use single quotes to protect the expression from the shell. (And remove the internal single quotes at the point of use, which are not expected by XPath)

xmlstarlet sel --var zgtag='"SuperTag"' -t -m "/profile-extension" -i '//tag=$zgtag'

In general, use single quotes for strings that do not need to be parsed by the shell, and double quotes otherwise. See What is the difference between the "...", '...', $'...', and $"..." quotes in the shell?


Worked example. I cannot test an application to your own command because I have neither valid input nor expected output for validating results. Instead I offer you this:

cat >xml <<'EOF'
<?xml version="1.0"?>
<outer>
  <inner x="a">Element A text</inner>
  <inner x="b">Element B text</inner>
</outer>
EOF

xmlstarlet sel -t --var e='"b"' -v '//inner[@x=$e]' -n <xml

Element B text

Chris Davies
  • 116,213
  • 16
  • 160
  • 287
  • 1
    I'm thinking the variable should be an XML string, i.e. 'SuperTag', including the single quotes, and that the use of $zgtag should be without single quotes in the expression. – Kusalananda Feb 07 '24 at 10:19
  • @Kusalananda no. Chris gave the good way AFAIK – Gilles Quénot Feb 07 '24 at 10:25
  • @Chris I tried out your version: --var zgtag="SuperTag" -t -m "/z:profile-extension" -i '//z:tag=$zgtag' -n -f, so I used double quotes for the variable definition and single quotes for the xpath expressions. I get this error: could not compile test expression '//z:tag=' – basZero Feb 07 '24 at 14:47
  • 1
    @chris-davis I added more info of my investigation into the question. – basZero Feb 07 '24 at 15:07
  • @basZero got it. The variable definition must include quotes, but the use point must not. Answer modified to reflect this – Chris Davies Feb 07 '24 at 16:22
  • Sorry @ChrisDavies, that neither works. Instead of your zgtag='"SuperTag"' it should be like this: zgtag="'SuperTag'", because in the resulting compiled XSLT we want the single quotes to be in... however overall it still does not work. – basZero Feb 08 '24 at 07:32
  • @basZero it works for me. If you provided a sample XML input that could be matched by your XPath in your question, along with expected output, I could test my changes. But at the moment I can only say that in my own testing environment it works – Chris Davies Feb 08 '24 at 08:17