4

The last few lines of my file /usr/share/glib-2.0/schemas/org.gnome.Vino.gschema.xml:

<schemalist>
  <schema>
   <!-- some other tags -->

    <key name='notify-on-connect' type='b'>
      <summary>Notify on connect</summary>
      <description>
        If true, show a notification when a user connects to the system.
      </description>
      <default>true</default>
    </key>

    <key name='enabled' type='b'>
      <summary>Enable remote access to the desktop</summary>
      <description>
      If true, allows remote access to the desktop via the RFB
      protocol. Users on remote machines may then connect to the
      desktop using a VNC viewer.
      </description>
      <default>false</default>
    </key>
  </schema>
</schemalist>

If I want to grep this paragraph:

<key name='enabled' type='b'>
  <summary>Enable remote access to the desktop</summary>
  <description>
  If true, allows remote access to the desktop via the RFB
  protocol. Users on remote machines may then connect to the
  desktop using a VNC viewer.
  </description>
  <default>false</default>
</key>

How should I use the grep command to achieve this?

2
  • 2
    Do you have to use grep ? If <key name='enabled' type='b'> only occur once, it would be easier to use awk (or perhaps sed or even bash). Or - of course something like python, perl, php, ... as in something that supports xml.
    – ibuprofen
    May 21, 2021 at 3:52
  • I'd advise against using grep for such tasks. grep is useful for parsing line-oriented data such as /etc/passwd, but it's not useful for parsing structured data like XML, JSON or YAML. If you want to parse XML, use tools designed for parsing XML (such as what others have posted, xq, xmlstarlet, xmllint, XSL, LibXML).
    – f4st
    May 22, 2021 at 15:17

6 Answers 6

13

Since your given example is a valid XML file, so I would use xq XML parser tool for that which is part of the yq installation package.

xq -x --xml-root key '
    .schemalist.schema.key[] | select(."@name" == "enabled")
' infile.xml

select the "key" tag if its "@name" attribute was equal to 'enabled'.

from the xq -h:

--xml-output, -x
Transcode jq JSON output back into XML and emit it
--xml-root XML_ROOT
When transcoding back to XML, envelope the output in an element with this name

8

Since you are dealing with valid XML, you can use xmlstarlet:

xmlstarlet sel -t -c "/schemalist/schema/key[@name='enabled']" infile.xml

This will query (sel) the XML document and print a copy -c of the XPATH element /schemalist/schema/key, where that XML node with attribute name set to enabled is selected.

Output for your example:

<key name="enabled" type="b">
      <summary>Enable remote access to the desktop</summary>
      <description>
      If true, allows remote access to the desktop via the RFB
      protocol. Users on remote machines may then connect to the
      desktop using a VNC viewer.
      </description>
      <default>false</default>
    </key>
0
6

To be clear:

This is most likely a job for some tool that understands XML documents, not grep or the like. There are good answers on this.


Perhaps:

grep

Using perl-regexp, and other additions.

grep -Pzo "(?s)\N*<key name='enabled'.*<\/key>\n" the_file.xml

Or a bit more limited (Is not capturing leading blanks ):

grep -zo "<key name='enabled'.*<\/key>."

awk

awk '
/<key name='\''enabled'\''/ { p=1 }
p { print $0 }
p && /<\/key>/ { exit }
' the_file.xml

Or

awk "/<key name='enabled'/,/<\/key>/" the_file.xml

sed

sed -n '/<key name='\''enabled'\''/,/<\/key>/p' the_file.xml
0
3

If you assume the file contains valid XML, you can use xmllint from the package libxml2-utils and find the desired XML element using XPath expression:

    $ xmllint --xpath "/schemalist/schema/key[@name='enabled' and @type='b']" /usr/share/glib-2.0/schemas/org.gnome.Vino.gschema.xml 
    <key name="enabled" type="b">
          <summary>Enable remote access to the desktop</summary>
          <description>
          If true, allows remote access to the desktop via the RFB
          protocol. Users on remote machines may then connect to the
          desktop using a VNC viewer.
          </description>
          <default>false</default>
        </key>
3

Another way to obtain the desired output is to use XSL (eXtensible Stylesheet Language) and xsltproc - which is installed by default if you are using the GNOME desktop - to transform the input document into the desired document (the output.)

Here is the stylesheet:

$ cat transform.xsl
<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method='xml' omit-xml-declaration='yes' />

<xsl:template match="/schemalist/schema/key[@name='enabled']">
    <xsl:copy-of select="." />
</xsl:template>

<xsl:template match='text()|@*'/>

</xsl:stylesheet>

Here is the output:

$ xsltproc transform.xsl input.xml
<key name="enabled" type="b">
      <summary>Enable remote access to the desktop</summary>
      <description>
      If true, allows remote access to the desktop via the RFB
      protocol. Users on remote machines may then connect to the
      desktop using a VNC viewer.
      </description>
      <default>false</default>
    </key>
$
3

Using Perl:

#!/usr/bin/perl

use 5.010;
use strict;
use warnings;
use XML::LibXML;

my $filename='/home/emmmcl01/gschema.xml';
my $dom = XML::LibXML->load_xml(location => $filename);
foreach my $key ($dom->findnodes('/schemalist/schema/key')) {
  my $desc = $key->findvalue('./description');
  my $nm = $key->findvalue('./@name');

  if ('enabled' eq $nm ) {
   print $key;
  }
}

This solution iterates through each key element and when it finds a key having a description whose "named" attribute is equal to "enabled" it prints out the element. The solution leverages a Perl XML parser: XML::LibXML which might have to be installed.

0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .