irish
irish

Reputation: 197

Fetch a particular word from an xml using batch script

I am trying to fetch a particular word from an xml by using sfk.exe tool. What it is doing is its fetching the whole sentence/line where the word exists instead of just that word. I want to fetch that word and store it in a temp file to be later stored in a variable. These words will change, basically its the name of the client and it would vary depending on which client's xml is it.

sfk find "C:\Env\Test\test.xml" "Name" > %temp%\Test.tmp

The above line of code fetches the following: <Org Updated="date" Owner="Test" Version="2/1/3/4"Database="Test" Client="Name">

So basically its getting me the whole line whereas i only want to get "Name" without the quotes.

Upvotes: 0

Views: 373

Answers (3)

JosefZ
JosefZ

Reputation: 30113

Although Windows batch can be kind of lame handling xml, this (quite simple) case could be solved using a (quite simple) script providing additional preliminary assumptions on structure of parsed line(s).

However, the following solution lays emphasis on (as far as possible) universal approach with no premises on position of Client= attribute inside the containing tag, or its unicity (singularity) inside a line, or number of preceding " double quotes etc. etc.:

@ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion
for /F "delims=" %%G in ('
      findstr /i /r "\<Client=" "D:\bat\SO\files\q44533501_input.xml"
  ') do (
    rem ECHO=merely debugging output 
        ECHO %%G
    set "_line=%%~G"
    call :lineFound
)
goto :eof

:lineFound
  rem remove cmd-poisonous characters `<` and `>` (replace them with spaces)
  set "_line=%_line:<= %"
  set "_line=%_line:>= %"

  set "_takeNextItem="
  rem parse 
  for %%g in ( %_line% ) do (
    if defined _takeNextItem (
      set "_takeNextItem="
      set "_client=%%~g"
      call :clientSet
    ) else (
      if /I "%%~g" == "Client" set "_takeNextItem=%%~g"
    )
  )
goto :eof

:clientSet
  rem ECHO=merely debugging output;
  rem      handle %_client% variable in desired manner instead 
      ECHO(%_client%
goto :eof

Of course, you could use sfk find "C:\Env\Test\test.xml" "Client=" command instead of my findstr /i /r "\<Client=" "D:\bat\SO\files\q44533501_input.xml" at the 4th line.

Sample output (includes debugging ECHO %%G to show possible variability of input xml file):

==> D:\bat\SO\q44533501.bat
<Org Updated="date" Owner="Test" Version="2/1/3/4" Database="Test" Client="Name">
Name
<Org Updated="date" Database="Test" Client="Name2" Owner="Test" Version="2/1/3/4">
Name2
<Org Updated="yesterday" Client="Name3"></Org><Org Client="Name4" Updated="today">
Name3
Name4

==>

Edit. To explain the :lineFound section, let's take look at FOR command (it is mostly used to process files, but you can also process text strings):

Conditionally perform a command on several files.

Syntax
      FOR %%parameter IN (set) DO command

Key
   set         : A set of one or more files, separated by any standard delimiter.
                 Wildcards can be used.

   command     : The command to carry out, including any command-line parameters.

   %%parameter : A replaceable parameter: e.g. in a batch file use %%G 
                                               (on the command line %G)

and utilise the fact that a xml line we want to parse contains just standard delimiters mentioned above in set description, see Delimiters:

Delimiters separate one parameter from the next - they split the command line up into words.

Parameters are most often separated by spaces, but any of the following are also valid delimiters:

  • Comma (,)
  • Semicolon (;)
  • Equals (=)
  • Space ( )
  • Tab ( )

Some example might help: for %g in ( %_line% ) do … loop processes, item by item, the _line variable after splitting it using standard delimiters as item separators. In principle, we obtain a sequence where each attribute name is followed by attribute value (but tag name as the first item):

==> for %g in ( %_line% ) do @echo %g
Org
Updated
"date"
Database
"Test"
Client
"Name2"
Owner
"Test"
Version
"2/1/3/4"

==>

Note that _line variable is hard-coded, for this particular sample case, as

==> set "_line=<Org Updated="date" Database="Test" Client="Name2" Owner="Test" Version="2/1/3/4">"

==> set "_line=%_line:<= %"

==> set "_line=%_line:>= %"

==> set _line
_line= Org Updated="date" Database="Test" Client="Name2" Owner="Test" Version="2/1/3/4"

Upvotes: 1

aschipfl
aschipfl

Reputation: 34899

Although batch files are not the best choice for processing XML data as they do not support them natively, I want to show you a way to do what you want:

@echo off
rem // Capture the output of the `find` command (`sfk` command is not necessary):
for /F "delims=" %%L in ('
    ^< "C:\Env\Test\test.xml" find /I " Client="
') do (
    rem // Store found line in variable:
    set "LINE=%%L"
    setlocal EnableDelayedExpansion
    rem /* Remove everything up to and including the first occurrence of ` Client`
    rem    from the string; then split off the leading `=` and the training `>`;
    rem    finally, remove the surrounding `""` from the remaining string: */
    for /F "delims==>" %%I in ("!LINE:* Client=!") do (
        endlocal
        rem // Return the extracted string:
        echo(%%~I
    )
)

This works only when there is a single occurrence of SPACE + Client in each applicable line, followed by an =-sign. Furthermore, this Client= attribute must be the last one in the containing tag, hence the whole attribute definition Client="Name" must be immediately followed by >.

Upvotes: 1

Compo
Compo

Reputation: 38589

If the line layout doesn't change then you could try this:

Set "Name=Microsoft"

For /F Tokens^=10^ Delims^=^" %%A in (
'Find /I "%Name%"^<"C:\Env\Test\test.xml"') Do Echo "%%A"

As a side note, were you to search for Client= you could probably simplify it further:

For /F Tokens^=10^ Delims^=^" %%A in (
'sfk find "C:\Env\Test\test.xml" "Client="') Do Echo "%%A"

I have used both the Find and sfk commands, they may be interchanged as you see fit.

Upvotes: 1

Related Questions