5

Ubuntu 14.04 Trusty Tahr.

Suppose I have directory called 'testmag' which may contain 100s of xml files and directories which in turn contain many xml files as well. I don't know names of any xml files but I know one of them contains tag <dbname>....</dbname>.

Now how to find the file containing the aforementioned tag and grep the tag's value as output in terminal

4 Answers 4

5

Here is a solution with find that will also output the filenames of files containing a match:

find . -name "*.xml" -exec grep '<dbname>' {} \;             \
                     -exec echo -e {}"\n" \;                 \
                     | sed 's/<dbname>\(.*\)<\/dbname>/\1/g'

Explanation

  1. find . -name "*.xml" find all xml files recursively from current directory
  2. -exec grep '<dbname>' {} \; on each file search for pattern <dbname>
  3. -exec echo -e {}"\n" \; echo filename + new line (-e option makes echo interpret \n)
  4. | sed 's/<dbname>\(.*\)<\/dbname>/\1/g' pipe output to sed to print only the field contained between the <dbname></dbname> tags.

NOTE1: you can format output in your echo -e ... to have results for each file clearly laid out, e.g. by adding new lines, or lines of underscore, whatever suits your need.

NOTE2: path to each file will be given relatively to . (e.g. ./subfolder1/file.xml). If you want absolute path, go for find $PWD -name ....

4
  • This exactly answers my query, perfect !!! Thanks @Valentin B.
    – Vicky Dev
    Oct 21, 2016 at 18:49
  • Well as far as question is concerned, your post answers it, no doubt, but just one query out of curiosity, if you don't mind. With above find command if I want to look for only <dbname>...</dbname> tag having <![CDATA[ as it's content's starting string and ignore the rest of the <dbname>... matches, how can I achieve it ?
    – Vicky Dev
    Oct 21, 2016 at 18:55
  • Hi Vicky, simply add the pattern in your grep command, ... -exec grep '<dbname><![CDATA[' {} \; ... Oct 24, 2016 at 9:21
  • This assumes that the dbname node's start and end tag are on the same line.
    – Kusalananda
    May 16, 2021 at 8:22
4

Using a proper XML parser to parse XML:

shopt -s globstar nullglob
for file in **/*.xml; do 
    dbname=$(xmlstarlet sel -t -v '//dbname' "$file")
    [[ -n "$dbname" ]] && printf "%s\t%s\n" "$file" "$dbname"
done
2

Using find with xq:

find testmag -type f -name '*.xml' -exec xq -r '..|(.dbname? // empty)' {} +

This would find all regular files with names matching *.xml in or below the testmag directory. For batches of these, xq would be called to extract the value of every dbname node found in those documents.

xq is a jq-like XML parser that is distributed with yq from https://kislyuk.github.io/yq/


Would you need the filenames of all XML files that have this node, you could use

find testmag -type f -name '*.xml' -exec xq -e '..|(.dbname? // empty)' {} \; -print

... although this would be a fair bit slower than just extracting the value of the node as we need to invoke xq once for each file.

2
  • Hi, one question, please. jq, yq, xq, these tools has no option for themselves for recursion? May 16, 2021 at 8:32
  • 1
    @αғsнιη You mean to recurse into directories? No, they are parsers. The task of providing the tools with filenames is left to the calling shell.
    – Kusalananda
    May 16, 2021 at 8:33
0

Suppose we have directory XMLS containing these files:

cat XMLS/file1
foo bar <dbname>target</dbname> baz
foo foo

cat XMLS/file2
<name>notarget</name>

I would use this command:

grep -r '<dbname>' XMLS/ | sed 's/.*<dbname>\(.*\)<\/dbname>.*/\1/'
target

Which, as you can see, returns the value inside the <dbname> tags. And not the value inside the <name> tags.


The -r flag for grep searches recursively.

sed strips the string of everything except the value target.

6
  • Can I use . for recursive search inside current directory ?
    – Vicky Dev
    Oct 21, 2016 at 11:08
  • @VickyDev Yes, . works fine. Oct 21, 2016 at 11:09
  • Why is target on second line ? Is it required in code ?
    – Vicky Dev
    Oct 21, 2016 at 11:11
  • @VickyDev What? No. That is the actual output from the command. Oct 21, 2016 at 11:12
  • @maulinglawns You should add g at the end of your sed instructions otherwise in case of multiple matches from grep only the first pattern will be processed. Oct 21, 2016 at 12:36

You must log in to answer this question.

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