0

This question already has an answer here:

I'm searching for a string in a massive XML file. Snip:

    <sv:node sv:name="notfound">
      <sv:property sv:name="jcr:primaryType" sv:type="Name">
        <sv:value>hst:component</sv:value>
      </sv:property>
      <sv:property sv:name="hst:referencecomponent" sv:type="String">
        <sv:value>hst:abstractpages/base</sv:value>
      </sv:property>
      <sv:node sv:name="main">
        <sv:property sv:name="jcr:primaryType" sv:type="Name">
          <sv:value>hst:component</sv:value>
        </sv:property>
        <sv:property sv:name="hst:template" sv:type="String">
          <sv:value>base-container</sv:value>
        </sv:property>
        <sv:node sv:name="container">
          <sv:property sv:name="jcr:primaryType" sv:type="Name">
            <sv:value>hst:containercomponent</sv:value>
          </sv:property>
          <sv:property sv:name="hst:xtype" sv:type="String">
            <sv:value>HST.vBox</sv:value>
          </sv:property>
          <sv:node sv:name="error">
            <sv:property sv:name="jcr:primaryType" sv:type="Name">
              <sv:value>hst:containeritemcomponent</sv:value>
            </sv:property>
            <sv:property sv:name="hst:componentclassname" sv:type="String">
              <sv:value>com.asdf.common.components.ErrorComponent</sv:value>
            </sv:property>
            <sv:property sv:name="hst:label" sv:type="String">
              <sv:value>Error</sv:value>
            </sv:property>
            <sv:property sv:name="hst:parameternames" sv:type="String" sv:multiple="true">
              <sv:value>errorCode</sv:value>
            </sv:property>
            <sv:property sv:name="hst:parametervalues" sv:type="String" sv:multiple="true">
              <sv:value>404</sv:value>
            </sv:property>
            <sv:property sv:name="hst:template" sv:type="String">
              <sv:value>errorpages/notfound</sv:value>
            </sv:property>
            <sv:property sv:name="hst:xtype" sv:type="String">
              <sv:value>HST.Item</sv:value>
            </sv:property>
          </sv:node>
        </sv:node>
      </sv:node>
    </sv:node>

The actual file is about 3000 lines long and contains multiple of these components. Needless to say, it's difficult to find stuff in it and the five other similar files.

A situation arose where I needed to find each top level component that contained a subcomponent with a certain name - "top level" being defined as an element that's indented four spaces. So I wrote a bash script using awk for this:

#!/bin/bash

file="$1"

values=$(awk '/errorpages\// {print NR,$0}' "$file")

OLDIFS=$IFS
IFS=$'\n'
for v in $values; do
    num=${v%% *}
    line=${v##* }
    awk -v num="$num" 'NR<=num && /^    </ {print NR,$0}' "$file" | tail -1
    echo "$v"
done
IFS=$OLDIFS

Running the script will output for example:

352     <sv:node sv:name="notfound">
679               <sv:value>errorpages/notfound</sv:value>
1147     <sv:node sv:name="genericerror">
1277               <sv:value>errorpages/genericerror</sv:value>
1147     <sv:node sv:name="unauthorized">
1445               <sv:value>errorpages/unauthorized</sv:value>

In other words, it found each top level component containing a subcomponent named "errorpages/whatever".

I'd like to make the string to search for a parameter:

#!/bin/bash

str="$1"
file="$2"

values=$(awk -v str="$str" '/str/ {print NR,$0}' "$file")

OLDIFS=$IFS
IFS=$'\n'
for v in $values; do
    num=${v%% *}
    line=${v##* }
    awk -v num="$num" 'NR<=num && /^    </ {print NR,$0}' "$file" | tail -1
    echo "$v"
done
IFS=$OLDIFS

The problem is, when I run the script like so:

$ ./find-xml-nodes.sh "errorpages\/" /code/website/config/workspace.xml

It gives me an error:

awk: warning: escape sequence `\/' treated as plain `/'

Followed by a bunch of lines that don't match.

Google suggests double escaping the slash like so:

$ ./find-xml-nodes.sh "errorpages\\/" /code/website/config/workspace.xml

That doesn't change the output, nor does triple escaping or quadruple escaping.

marked as duplicate by David Kennedy, don_crissti, Community May 28 '15 at 21:44

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • Take a look at xmllint or xmlstarlet. – Cyrus May 28 '15 at 20:55
  • Better yet, take a look at xml2. It's perfect for simple searches like this. There is also xml-coreutils, but it's substantially more complex, and yet its handling of XPath is too crude to be really useful. – lcd047 May 28 '15 at 21:09
  • Note that /str/, where str is a variable, does not work as you expect it. The syntax /.../ defines regexp constants. To compare against regexp in a variable use, e.g., $0 ~ str. - With respect to backslashes note that in strings \ is an escape character, so needs escaping \\; but the implicit resulting \ is again a escape character to the regexp engine. AFAIR, this is described in the GNU awk manual. - Generally have a look at xgawk, a branch of gawk with XML processing facilities. In newer gawks look for gawkextlib for the XML extension lib. – Janis May 28 '15 at 21:10
  • @Koveras - Pass shell variable as a /pattern/ to awk... but unless I'm missing something this could be easily done with grep/sed. Give it a try : grep -n . infile | sed -n '/^[0-9]*: </h;/errorpages\//{H;x;p}' (there are four spaces between : and <) – don_crissti May 28 '15 at 21:40
  • @don_crissti that is what I was looking for. Guess this one's a dupe. – David Kennedy May 28 '15 at 21:42