I'm working with XML files, each of which could be dozens of lines long. There are literally hundreds of these files, all over a directory structure. Yes, it is Magento.

I need to find the file that has the <foo><bar><boom><bang> element. A <boom><bang> tag could be defined under other tags, so I need to search for the full path not just the end tag or tags. There could be dozens of lines between each tag, and other tags between them:

... 50 lines ....
... 50 lines ....
      <bang>Vital information here</bang>

What is the elegant, *nix way of searching for the file that defines <foo><bar><boom><bang>? I'm currently on an up-to-date Debian-derived distro.

This is my current solution, which is far from eloquent:

$ grep -rA 100 foo * | grep -A 100 bar | grep -A 100 boom | grep bang | grep -E 'foo|bar|boom|bang'

You could try xmlstarlet to select if the path exists then output the filename:

find . -name '*.xml' -exec xmlstarlet sel -t -i '/foo/bar/boom/bang' -f -n {} +
  • @dotancohen bear in mind I'm a beginner with xml/xmlstarlet and there may be things wrong with this approach... Aug 1 at 14:42
  • Certainly, but they're far less wrong than my current grep-based solution. If I improve on this I'll add a comment or edit. Thank you.
    – dotancohen
    Aug 1 at 14:52

Using xq XML parser tool from the yq installation package and print the filename only when exit status was 0 based on the xq output.

find -type f -name '*.xml' -exec sh -c '
    xq -e '.foo.bar.boom.bang' "$1" >/dev/null
' _ {} \; -print

-e set the exit status code based on the output;

from the man jq for more details about this option:

-e / --exit-status:
Sets the exit status of jq to 0 if the last output values was neither false nor null, 1 if the last output value was either false or null, or 4 if no valid result was ever produced. Normally jq exits with 2 if there was any usage problem or system error, 3 if there was a jq program compile error, or 0 if the jq program ran.

  • Thank you, xy seems a very useful tool! I have used jq in the past. Is there a way to add wildcards in the element tree, i.e. .foo.*.bang (example which does not work)? Googling I don't see a simple solution, but if you know offhand than I would appreciate it. Thank you.
    – dotancohen
    Aug 2 at 9:45
  • @dotancohen I'm not much familiar in these commands just a little bit I know, by cases whenever I see questions like this I'm going looking into their man pages to see if there are relevant things there that can resolve asker question or not. I'm not sure if these commands can supports wildcards or not, but I know it has some functions for regex matching but never used them. you may also have a look there too. and you are welcome! that's also xq, yq not xy by the way. Aug 2 at 11:32
  • Thank you. Yes, I did check before asking, but the best that I found was a complicated workaround for jq.
    – dotancohen
    Aug 2 at 12:11
  • @dotancohen see also here stedolan.github.io/jq/manual/#RegularexpressionsPCRE for regex matching in jq, which I think it should apply in xq too (again didn't test or used before) Aug 2 at 15:31

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.