2

I want to extract the contents of fqdns from the following file, but only for each device where status is new:

    <device id='10.72.48.215'>
            <address>10.72.48.215</address>
            <status>old</status>
            <fqdns>10.72.48.215</fqdns>
            <parent></parent>
            <type>Unknown</type>
            <ports>
            </ports>
            <operatingsystems>
            </operatingsystems>
    </device>
    <device id='10.72.48.216'>
            <address>10.72.48.216</address>
            <status>new</status>
            <fqdns>10.72.48.216</fqdns>
            <parent></parent>
            <type>Unknown</type>
            <ports>
            </ports>
            <operatingsystems>
            </operatingsystems>
    </device>

So, for the above, I want to obtain 10.72.48.216 (and not 10.72.48.215).

  • 2
    Can you provide the output you want to extract from this xml file? I don't understand which part you mean. – chaos Dec 8 '15 at 15:57
  • 1
    I do not understand it too, – Rui F Ribeiro Dec 8 '15 at 15:57
  • 1
    so basically, i have this long xml file with the same contents as above. some have "<status>old</status>" some has "new". But i just want to grab the "new" one. " <status>new</status> <fqdns>10.72.48.151</fqdns> <status>new</status> <fqdns>10.72.48.152</fqdns> <status>new</status> <fqdns>10.72.48.153</fqdns>" something like so – Hai Le Dec 8 '15 at 16:00
  • Your example has only old ones. – user unknown Dec 8 '15 at 16:05
  • pcregrep -M "\<status.*\n.*fqdns\>$" file.xml – Rui F Ribeiro Dec 8 '15 at 16:06
3

If you add an XML declaration and enclose the two device elements in a top-level element, you can process your file with XPath:

$ cat ./248127.xml
    <device id='10.72.48.215'>
            <address>10.72.48.215</address>
            <status>old</status>
            <fqdns>10.72.48.215</fqdns>
            <parent></parent>
            <type>Unknown</type>
            <ports>
            </ports>
            <operatingsystems>
            </operatingsystems>
    </device>
    <device id='10.72.48.216'>
            <address>10.72.48.216</address>
            <status>new</status>
            <fqdns>10.72.48.216</fqdns>
            <parent></parent>
            <type>Unknown</type>
            <ports>
            </ports>
            <operatingsystems>
            </operatingsystems>
    </device>

$ ( echo '<?xml version="1.0"?><doc>'; cat ./248127.xml ; echo '</doc>' ) \
    | xpath -q -e '//device[status/text()="new"]/fqdns'
<fqdns>10.72.48.216</fqdns>
  • Toby, how i have both "old" and "new" status in the file. How do I just get the "new" one? – Hai Le Dec 8 '15 at 16:17
  • Modify the XPath expression appropriately - e.g. xpath -q -e '//device[status/text()="new"]/fqdns' – Toby Speight Dec 8 '15 at 16:34
  • thxs this also works – Hai Le Dec 8 '15 at 18:30
1

You can do it with a pcrgrep command, which has an option for multiline search.

pcregrep -M "\<status.*\n.*fqdns\>$"

As for the explanation you requested:

So -M for multiline

\< status for the beggining of string. I have to use \ to take the meaning out of < as it is special to the shell.

Then . (any character) followed by * with it means it can be repeated.

\n for a new line

. for a character followed by * as it can be repeated.

then the string fqdns

and to close

\> which again is >, with \ to take the meaning

and finally, $ is the end of line

The strings matching is know as regexp

https://en.wikipedia.org/wiki/Regular_expression

  • You are welcome. Please peruse the wikipedia entry to be able to build your own regexp expressions. – Rui F Ribeiro Dec 8 '15 at 16:23
1

A simple grep can do the trick:

grep -A1 "<status>new" sample.xml 
# <status>new</status>
# <fqdns>10.72.48.216</fqdns>

which is only recommendet if the source of your xml is stable and doesn't change the order of lines. (I modified your example to have a 'new' in the second block.)

You may filter the following line with more grep:

grep -A1 "<status>new" sample.xml | grep "<fqdns>" 
# <fqdns>10.72.48.216</fqdns>

With sed, you can easily extract just the IP:

sed -rn '/<status>new/{n; s/.*<fqdns>(.*)<\/fqdns>/\1/p}' sample.xml
# 10.72.48.216

It matches <status>new and reads the next line and substitutes <fqdns> and the end tag with the part in betweeen. The -n tells sed to 'no printing by default', the '-r' is to allow regular expressions, here the parens around .*, to be written without masking.

  • This is awesome. this is exactly what I needed. Thanks – Hai Le Dec 8 '15 at 16:27

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.