1

For each node list having Lists as its parent node, I want to get/save into a variable values of the first three node, namely entry, output and token. I know how to do that with vbscript, but it is much more interesting to me to have a solution in batch. Please, can it be done?

   <list1>
      <entry>myEntry</entry>
      <output>myOut</output>
      <token>4</token>
              <status>1</status>          
              <number>6</number>
      <!-- Comments -->       
       </list1>

     <list2>
      <entry>newEntry</entry>
      <output>thisOutput</output>
      <token>1</token>
              <status>0</status>          
              <number>1</number>
      <!-- Comments -->       
     </list2>

             <list3>
      <!-- repeat nodes as before -->         
     </list3>

Any help!!! Thanks

Subham Tripathi
  • 2,683
  • 6
  • 41
  • 70
tamo
  • 51
  • 2
  • 5

2 Answers2

1
@ECHO OFF
SETLOCAL
CALL :zapvars
FOR /f "tokens=2,3delims=<>" %%i  IN (myxml.xml) DO (
CALL :analyse %%i
IF DEFINED tlist   SET   list=%%i
IF DEFINED tentry  SET  entry=%%j
IF DEFINED ttoken  SET  token=%%j
IF DEFINED toutput SET output=%%j
)

GOTO :eof

:analyse
FOR %%a IN (tlist tentry ttoken toutput) DO (SET %%a=)
ECHO %1|FINDSTR /b "list" >NUL
IF NOT ERRORLEVEL 1 SET tlist=Y&GOTO :EOF 
IF "%1"=="entry" SET tentry=Y&GOTO :EOF 
IF "%1"=="output" SET toutput=Y&GOTO :EOF 
IF "%1"=="token" SET ttoken=Y&GOTO :EOF 
IF NOT "%1"=="/%list%" GOTO :EOF 
:: Found end of list
ECHO list=%list% entry=%entry% output=%output% token=%token%
:zapvars
FOR %%z IN (list entry output token) DO (SET %%z=)
GOTO :eof

Really not that hard. Question is what you want to do with it once it's in the envvars. Obviously, if you want to check for missing elements, all you need do is use the results only if defined list if defined entry if defined output if defined token

Given the input format, each line is tokenised using < and > The first selected token is applied to %%i and the second to %%j. The first token on the line is the leading spaces.

%%i therefore will be the node name. For each line, the node name is passed to the subroutine :analyse for er, analysis.

:analyse first clears each of the flags tname meaning token is aname. First cab off the rank is to see whether the token startslist, so the token isECHOed intoFINDSTRwhich looks for a line beginning (/b`) "list". If findstring finds what it's looking for, ERRORLEVEL is set to 0, else non-zero.

If errorlevel is not 1 or greater then TLIST is set to Y It could be set to anything - just long as it's set to SOMETHING. The subroutine then exits.

If it wasn't a token beginning list then :analyse loos for each of the target tokens. If it finds one, it sets the appropriate flag.

Finally, if the token isn't /LISTNAMEBEINGPROCESSED then the routine exits. If /list... IS found, then the value-tokens are displayed and then cleared.

Meanwhile, back in the FOR loop, following a call to :analyse, the routine's decision is contained in at most ONE of (tlist,tentry,ttoken,toutput) beging SET in the environment. If tname is set, then the corresponding value-token is assigned from the appropriate metavariable - %%i - the listname if tlist is set, and %%j - the data item for the others. For the nodes that are of no interest, no flags are returned by :analyse so the FOR loop simply proceeds to the next line.

Magoo
  • 77,302
  • 8
  • 62
  • 84
  • I want to pass each of them (values from entry, output,token) to another .bat. Do you think it is the best way? I don't really know. Please let me tell you that i'm amazed by your quick answer!!!! – tamo Mar 30 '13 at 13:11
  • Please can you comment a little your code? Indeed what is the point of tlist,tentry,ttoken,toutput into DEFINED? Thanks – tamo Mar 30 '13 at 13:22
  • Well, batch is quick and easy to write in. It may be a bit cranky so you've got to treat it with teh respect due it age. You could call your subsidiary batch by simply replacing the `ECHO` line that's delivering the output with `CALL yourbatch` and then YOURBATCH will run with the variables `list`,`entry`,`token` and `output` already established. Note that since `yourbatch` is an **EXTERNAL** routine, you don't precede it with a colon. – Magoo Mar 30 '13 at 13:50
  • Thanks Pet for these details. Forgive me because my xml file begin always with XX XX XX ........ I suppose that token will be also changed? – tamo Mar 30 '13 at 14:50
  • Should process just fine - anything before `` will be ignored. It won't even try to process `` since it's specifically looking for lower-case. It will process `` until `` and produce an output line (or call your batch) when `/list1>` is read. It will then pick the lines from the next ` .. ` Easiest way is to try it...just display the data and verify before invoking your batch. – Magoo Mar 30 '13 at 15:20
1

The Batch file below get/save the values of the desired nodes and process they for each parent node. This method allows to modify the number and names of the processed nodes by just changing one line in the program and it does not use any external command (*.exe file) nor call command, so it is fast.

@echo off
setlocal EnableDelayedExpansion
rem Define the names of the desired nodes
set nodes=entry output token
rem Process file lines and get first two tokens separated by <> (ie: %%a=entry, %%b=myEntry)
for /F "tokens=2-3 delims=<>" %%a in (theXMLfile.xml) do (
   set "node=%%a"
   rem If this node is not the end of this record...
   if "!node:~0,5!" neq "/list" (
      rem If this node is one of the desired ones...
      if "!nodes:%%a=!" neq "%nodes%" (
         rem Assign this node into a variable of same name (ie: set entry=myEntry)
         set "%%a=%%b"
      )
   ) else (
      rem ListX node complete: process it, for example:
      ECHO call another.bat !entry! !output! !token!
   )
)

However, if the input file is very large, the call command executed with each record may make the program to take too long. A modification that allows the program to run faster is to store all nodes in arrays, instead of individual variables, and then pass the number of parent nodes to the subroutine that is called just once.

@echo off
setlocal EnableDelayedExpansion
rem Define the names of the desired nodes
set nodes=entry output token
rem Define the index of the next array element
set index=1
rem Process file lines and get first two tokens separated by <> (ie: %%a=entry, %%b=myEntry)
for /F "tokens=2-3 delims=<>" %%a in (theXMLfile.xml) do (
   set "node=%%a"
   rem If this node is not the end of this record...
   if "!node:~0,5!" neq "/list" (
      rem If this node is one of the desired ones...
      if "!nodes:%%a=!" neq "%nodes%" (
         rem Assign this node into THE CURRENT ELEMENT OF AN ARRAY variable of same name (ie: set entry[!index!]=myEntry)
         set "%%a[!index!]=%%b"
      )
   ) else (
      rem ListX node complete: pass to next element of arrays
      set /A index+=1
   )
)
rem Call the subroutine and pass as parameter the number of parent listX nodes
set /A number=index-1
call :anotherSub %number%
goto :EOF

:anotherSub numberOfNodes
rem Process all nodes, for example:
for /L %%i in (1,1,%1) do (
   echo !entry[%%i]! !output[%%i]! !token[%%i]!
)
exit /B

Antonio

Aacini
  • 65,180
  • 12
  • 72
  • 108
  • Antonio, I have understand logic of your script but running it with theXMLfile.xml (my file xml) , echo !entry[%%i]! !output[%%i]! !token[%%i]! does not show nothing from promt command line. Why? Othewise could you explain me what the point of "!node:~0,5!"? Thanks – tamo Apr 01 '13 at 11:13
  • Antonio, when debugging your script, i notice that we never get into else ( rem ListX node complete: pass to next element of arrays set /A index+=1) So index is not properly raised well. Please how to correct that? Thanks in advance – tamo Apr 01 '13 at 20:38
  • Moreover I thought the 'else' command could not exist in Batch, What about??? Thanks in advance – tamo Apr 01 '13 at 20:47
  • @tamo: `!node:~0,5!` get the first 5 characters of node, excluding the number. Perhaps are the names of the parent nodes written in uppercase letters? If so, include an /I option in the IF command this way: `if /I "!node...`. Does the first program run correctly? You may insert `echo` commands at several places to trace the program execution. – Aacini Apr 02 '13 at 15:50