0

I have written following batch script, which takes input.xml, modifies it, outputs the new file to temp.xml, and then copies temp.xml to input.xml.

It is working fine for me but sometimes it copies an empty temp.xml file to input.xml, which results in me losing my data.

Here is the code:

@echo off
setlocal EnableDelayedExpansion
set dt=%date:~7,2%-%date:~4,2%-%date:~10,4%_%time:~0,2%-%time:~3,2%-%time:~6,2%
cd C:\test\inputFiles
(for /F "delims=" %%a in ('findstr /I /L "<initialTransferReference>" input.xml') do (
set "line=%%a"
set "line=!line:*<initialTransferReference>=!"
for /F "delims=<" %%b in ("!line!") do (
set a=%%b
)))
set /a newvalue=a+1
(for /F "delims=" %%a in (input.xml) do (
set "line=%%a"
set "newLine=!line:initialTransferReference>=!"
if "!newLine!" neq "!line!" (set "newLine=<initialTransferReference>%newvalue%</initialTransferReference>"
)
echo !newLine!
)) > temp.xml
copy /y temp.xml input.xml
del temp.xml
copy /y input.xml "C:\test\modifiedFile\input_%dt%.xml"

Why does the script sometimes copy an empty temp.xml file to input.xml?

SomethingDark
  • 13,229
  • 5
  • 50
  • 55
  • Do you have the literal string `` in your code or are you using that as a substitute for what you're actually using? – SomethingDark Jun 02 '15 at 06:55
  • 1
    `for /F "delims=" %%a in (input.xml) do (` will fail raising `errorlevel 1` and supposedly displaying `The system cannot find the file input.xml` message in case of locked input file. Read great dbenham's answer to [How to check in command-line if a given file or directory is locked](http://stackoverflow.com/a/10520609/3439404) – JosefZ Jun 02 '15 at 18:38
  • @JosefZ - Good thinking - that could very well be the source of the problem. I think you should write it up as an answer. I had completely forgotten about that FOR /F behavior. It is somewhat unexpected because the FOR /F loop only reads the file, it does not write, and most read only operations do not lock the file. – dbenham Jun 02 '15 at 20:11
  • @dbenham - Yes, the way the `FOR /F` treats a locked file seems to be a bit weird as e.g. `findstr` or `type` against the same locked file returns right result... – JosefZ Jun 02 '15 at 20:28
  • @SomethingDark in input.xml there is a line 1234 – user3081593 Jun 03 '15 at 10:23

2 Answers2

0

JosefZ may be correct in his comment - Your file will be overwritten by an empty temp.xml if input.xml is locked by another process. But the lock must be temporary, otherwise the COPY command will fail as well, and your original will be unchanged.

The only other potential reason I can think of is if your input.xml was already empty at the start of the process. But then an empty input.xml should be the expected result.

The following minimal change will prevent the lock problem. I preserved as much of your code as possible, except I reformatted the spacing and line breaks to better show the logic with all of those parentheses.

I put an extra set of parentheses around the 2nd loop, and conditionally MOVE and COPY the files only if the inner loop was successful. Otherwise I delete the temp file, and the original remains unchanged and the dated copy is not created. I also used MOVE instead of COPY followed by DEL.

Note that the 2ND FOR /F loop also returns failure if it was not able to read any lines, so the dated copy will not be created if input.xml starts out as empty. But if the <initialTransferReference> line is not found, then no change is made, yet the dated copy will still be created.

@echo off
setlocal EnableDelayedExpansion
set dt=%date:~7,2%-%date:~4,2%-%date:~10,4%_%time:~0,2%-%time:~3,2%-%time:~6,2%
cd C:\test\inputFiles
(
  for /F "delims=" %%a in ('findstr /I /L "<initialTransferReference>" input.xml') do (
    set "line=%%a"
    set "line=!line:*<initialTransferReference>=!"
    for /F "delims=<" %%b in ("!line!") do (
      set a=%%b
    )
  )
)
set /a newvalue=a+1
(
  (
    for /F "delims=" %%a in (input.xml) do (
      set "line=%%a"
      set "newLine=!line:initialTransferReference>=!"
      if "!newLine!" neq "!line!" (
        set "newLine=<initialTransferReference>%newvalue%</initialTransferReference>"
      )
      echo !newLine!
    )
  ) > temp.xml
) && (
  move /y temp.xml input.xml >nul
  copy /y input.xml "C:\test\modifiedFile\input_%dt%.xml" >nul
) || del temp.xml 2>nul

Your code could be dramatically simplified. For example, you could easily do everything in one loop.

Also, your code is highly dependent on the format of the XML. It will fail if there are multiple tags on the <initialTransferReference> line, or if the tag is split across multiple lines. It will also fail if the file contains !.

A much more robust solution could be written with batch, but I wouldn't bother - it quickly becomes a complicated, arcane mess.

I propose you use my JREPL.BAT regular expression text processor instead :-)
JREPL.BAT is pure script (hybrid JScript/batch) that runs natively on any Windows machine from XP onward. The solution could be as simple as:

@echo off
setlocal
set dt=%date:~7,2%-%date:~4,2%-%date:~10,4%_%time:~0,2%-%time:~3,2%-%
call jrepl "\d+(?=\s*</initialTransferReference>)" "Number($0)+1" /j /m /f "C:\test\inputFiles\input.xml" /o - && copy /y "C:\test\inputFiles\input.xml" "C:\test\modifiedFile\input_%dt%.xml"

The above looks for a number that precedes the closing tag, and replaces it with the incremented value. It doesn't care if there additional tags on the line. It even allows the value and closing tag to be on separate lines.

The dated copy is only created if JREPL successfully modified the input.xml. It will not be created if input.xml was locked, or if it did not make any changes because no integer value was found before </initialTransferReference>.

Like most regex XML "solutions", it is still possible to throw valid XML at it that can fail, but you would probably have to go out of your way to make it fail. It will most likely work with any real world data that you throw at it.

If you really want a robust solution, then you should use VBS, JScript, or PowerShell to properly parse and manipulate the XML.

I don't like how you derive your timestamp value because it is dependent on the date and time formats used by your locale. But that is an entirely different topic.

Last update in response to question in comment

You can use one more JREPL instead of XCOPY to append the date string to the Filename tag and write the output file in a single step. Note my use of line continuation to make the code easier to read.

@echo off
setlocal
set dt=%date:~7,2%-%date:~4,2%-%date:~10,4%_%time:~0,2%-%time:~3,2%-%
call jrepl "\d+(?=\s*</initialTransferReference>)" "Number($0)+1" /j /m /f "C:\test\inputFiles\input.xml" /o - && ^
call jrepl "(?=\s*</FileName>)" "_%dt%" /m /f "C:\test\inputFiles\input.xml" /o "C:\test\modifiedFile\input_%dt%.xml"
dbenham
  • 127,446
  • 28
  • 251
  • 390
  • Thanks for making my stuff so easy(using JREPL). Can you plz explain in which case JREPL will fail, do you mean when input.xml is locked? – user3081593 Jun 03 '15 at 10:21
  • @user3081593 - I've updated my answer - the JREPL solution is modified slightly, and I tried to better explain the limitations. – dbenham Jun 03 '15 at 17:53
  • Now things are almost clear to me(i am newbie to batch). Using JREPL, Can I update another tag which contains a string(xyz)? I want that xyz would be updated to xyz_date&time(same as %dt%) everytime I run this script? – user3081593 Jun 04 '15 at 09:34
  • can you suggest me something to do what I requested in above comment. I want to add one more thing, the tag xyz is in the same input file and I want to update both tags in one instance. – user3081593 Jun 10 '15 at 13:03
  • @user3081593 - Suppose the FileName is xyz.ext - should it become xyz_date&time.ext, or xyz.ext_date&time? – dbenham Jun 10 '15 at 14:27
  • All this time I had lots of work and I forgot to look into it. Actually the file name is like "ful.pain.cfonb160.sct" so I want to add "_date&time" at the end of complete file name (ful.pain.cfonb160.sct_date&time). – user3081593 Jun 17 '15 at 12:52
  • @user3081593 - See my update in the answer. If you have any more questions, then, err.... ask another question ! – dbenham Jun 19 '15 at 20:46
0

for /F "delims=" %%a in (input.xml) do ( ... will fail raising errorlevel 1 and supposedly displaying The system cannot find the file input.xml message in case of locked input.xml file.

Combined great dbenham's answer and lukk's answer to the How to check in command-line if a given file or directory is locked question: a check loop embedded in your code as follows. Unattended with timeout...

@echo off
setlocal EnableDelayedExpansion
set dt=%date:~7,2%-%date:~4,2%-%date:~10,4%_%time:~0,2%-%time:~3,2%-%time:~6,2%
pushd C:\test\inputFiles

:locktest
set "_locktest="
((call )>>"temp.xml" ) 2>nul || set "_locktest=locked"
If defined _locktest (
  echo file is locked; unlock it, please
      rem pause instead of timeout in the next  line?
  timeout /T 5 >NUL 2>&1
  goto :locktest
)
::::::::::::::::::::::::::::::::::
::  your script remainder here  ::
::::::::::::::::::::::::::::::::::
Community
  • 1
  • 1
JosefZ
  • 28,460
  • 5
  • 44
  • 83