2

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 1 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

3

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 ....

  • This exactly answers my query, perfect !!! Thanks @Valentin B. – Vicky Dev Oct 21 '16 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 '16 at 18:55
  • Hi Vicky, simply add the pattern in your grep command, ... -exec grep '<dbname><![CDATA[' {} \; ... – Valentin B. Oct 24 '16 at 9:21
1

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.

  • Can I use . for recursive search inside current directory ? – Vicky Dev Oct 21 '16 at 11:08
  • @VickyDev Yes, . works fine. – maulinglawns Oct 21 '16 at 11:09
  • Why is target on second line ? Is it required in code ? – Vicky Dev Oct 21 '16 at 11:11
  • @VickyDev What? No. That is the actual output from the command. – maulinglawns Oct 21 '16 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. – Valentin B. Oct 21 '16 at 12:36
0

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

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.