0

we can find xml file type as the follwing

find /tmp/ -type f -name '*.xml'

but how to change the syntax in order to find only xml that contain with:

<Name>some words</Name>

and print what is between:

<Name> ------ </Name>

expected output

some words      

example - xml file contain:

<Name>files_with_extra_data</Name>

expected output

files_with_extra_data
1

A simple solution would be to use sed:

find /tmp -name '*.xml' -exec sed -n 's/<Name>\([^<]*\)<\/Name>/\1/p' {} +

The regex matches the tags and prints what is in between. If we remove the scape characters is easier to read:

s / <Name>([^<]*)</Name> / \1 

The parenthesis matches any character which is not "<" and gets mapped to \1

As mentioned in the comments this would be a simple solution, regular expressions can not cope with all of the possible variations of structured text. So if you have multiple lines between tags or other tags it won't work and you will need to use a real xml parser

  • 2
    Not if the XML tag can span multiple lines. – Weijun Zhou Jan 17 '18 at 8:48
  • @WeijunZhou True, but I think that it's not the problem that he was describing though – Blasco Jan 17 '18 at 8:51
  • 1
    The input is an xml file. It's always going to be a potential problem. regular expressions can not cope with all of the possible variations of structured text. that's why you need to use a real xml parser. – cas Jan 17 '18 at 8:53
  • @cas I added your warnings, I still thing this solution might be of use for him – Blasco Jan 17 '18 at 8:56
  • 1
    this syntax is working - find /tmp -name '*.xml' -exec sed -n 's/<Name>([^<]*)<\/Name>/\1/p' {} + – yael Jan 17 '18 at 9:06
1

For a simple XML job like this, I'd use xml2 and cut. (or sed, or awk, or perl).

find . -iname '*.xml' -exec bash -c 'xml2 < {}' \; | grep '/Name=' |
  cut -d '=' -f2-

or

find . -iname '*.xml' -exec bash -c 'xml2 < {}' \; | sed -n -e 's/^[^=]*\/Name=//p'

or

find . -iname '*.xml' -exec bash -c 'xml2 < {}' \; | 
  awk -F'=' '/Name=/ {$1=""; sub(/^ /,"",$0); print }'

(The sub() function call in the awk version strips the leading space left after setting $1 to "" - awk doesn't have a way of deleting fields from the input line, the best you can do is set it to the empty string and clean up afterwards. Alternatively, split() the line into an an array, delete the field(s) you don't want, and then join the array into a string for printing. awk doesn't have a join() function like perl so you'll have to write your own)

or

find . -iname '*.xml' -exec bash -c 'xml2 < {}' \; |
  perl -F= -lane 'if (m:/Name=:) { delete @F[0]; print @F}'

xml2 converts XML formatted data into a line-oriented format suitable for processing with line-oriented text utilities like awk, or sed, or perl and many others. It comes with a corresponding 2xml program which can convert that line-oriented format back to properly formatted XML.

For more complicated tasks, I'd use xmlstarlet

xmlstarlet is an XML processing tool that you can use to list, query, extract, and modify data in XML files.

Both are available packaged for debian and other Linux distros.


The, IMO, best solution is to use a language like perl or python that has an XML parsing library, and use that. xmlstarlet is great for working with XML files in shell, but constructing the command-line for very complicated searches becomes more work (and much harder to read and debug) than just writing a script in perl or python to do the job. That's partly because I do a lot more programming in those languages and find it much easier to work with...but mostly because IMO it's better to concentrate your learning effort on general-purpose languages that can be used for a wide variety of tasks than on domain-specific languages/tools that can only be used for one very specific thing.

  • not all linux have xml2 , and in my case need to download it ( bur prefer not to do that ) , and use the default cli – yael Jan 17 '18 at 9:03
  • I just came back to add that for really complicated XML tasks, I'd use a language that had XML parsing libraries. Like perl, or python. Both have several to choose from, of varying complexity and capability. I'm inclined to use perl, so I'd use XML::Parser or XML::Twig or XML::Simple depending on what I needed to do. That ("use a language with an XML lib") is the correct solution if you don't have xml2 or sxmltarlet. – cas Jan 17 '18 at 9:09
  • “… that contain [sic] specific tag name …” I believe that a correct answer must search for Name. – Scott Jan 18 '18 at 0:09
  • @Scott that somehow got dropped when i copy-pasted from my terminal and i didn't notice. thanks for pointing it out. – cas Jan 18 '18 at 1:16
0

With pcregrep:

pcregrep -rMh --include='\.xml\z' -o1 '(?s)<name>(.*?)</name>' .
  • pcregrep not installed on my linux , so we want to use the ordinary commands – yael Jan 17 '18 at 11:04
-2

Use below command to achieve the mentioned result. Tested and it worked fine

I have just pipelined your output to awk delimitter

find /tmp/ -type f -name '*.xml' | awk -F ">" '{print $2}'  | sed "s/<.*//g"
  • 2
    You tested … what?  You seem not to understand what the question is asking for.  … … … … … … … … You’ve posted 61 answers so far this year (for an average of slightly more than 3.5 per day).  I advise you to focus more on quality and less on quantity.  When there are other answers posted before yours, you might want to read them before you say anything, and verify that your interpretation of the question is the same as other peoples’. – Scott Jan 18 '18 at 0:10

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.