qloq
qloq

Reputation: 1275

How to fix waiting for a specific value of variable

I'm trying to do some job for every file in a folder, wait until a property of virtual machine is set to a specific value and do some other tasks:

ECHO off
setlocal enabledelayedexpansion

SET VM_NAME=Win10-Pro-x32
SET TESTS_FOLDER=C:\tests
SET STATE=
SET FINISHED_STATE=Finished

FOR %%f IN (%TESTS_FOLDER%\*) DO (
    echo "doing some task"
    :checking_loop
    IF !STATE! NEQ !FINISHED_STATE! call :check_state
    echo "doing some other task"
)

goto :eof

:check_state
    FOR /f "tokens=*" %%i IN ('VBoxManage guestproperty get %VM_NAME% "State"') DO SET STATE=%%i
    SET STATE=%STATE:~7%
    ping 127.0.0.1 -n 2 > nul
    goto :checking_loop

This code outputs "doing some task" and continue doing something else (I mean it not prints anything else and not stops), how to fix that?

UPDATE: Some clarifications for Mofi: I need to wait for STATE property in all iterations - this property means that my script finished in guest VM. So, if I have 2 files in target folder batch file should do something like that:

(Iteration 2)

UPDATE 2: I had tired to deal with batch, so I rewrote my script using Python. However, I still interested to get how to do it using batch file.

Upvotes: 0

Views: 51

Answers (1)

Mofi
Mofi

Reputation: 49086

There could be perhaps used the following code:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

set "VM_NAME=Win10-Pro-x32"
set "TESTS_FOLDER=C:\tests"
if exist %SystemRoot%\System32\timeout.exe (
    set "WaitASecond=%SystemRoot%\System32\timeout.exe /T 1 /NOBREAK"
) else (
    set "WaitASecond=%SystemRoot%\System32\ping.exe -n 2 -w 1000 127.0.0.1"
)

for %%I in ("%TESTS_FOLDER%\*") do (
    echo doing some task
    call :wait_for_state
    if not errorlevel 1 (
        echo doing some other task
    ) else (
        echo Do something on state Finished not returned within 60 retries.
    )
)
rem Exit the batch file processing without a fall through to the subroutine.
exit /B

rem The subroutine below exits with value 0 on executed program/script has
rem output a string containing anywhere case-insensitive the word Finished.
rem The program/script is otherwise executed after a delay of one second
rem once more. But the retry loop is exited after 60 retries which is
rem approximately 60 seconds (one minute) on VBoxManage finishes always
rem quickly in a few milliseconds to avoid an endless running retry loop.
rem The value 1 is returned in this error case by the subroutine to the
rem calling FOR loop above which should handle this error case appropriately.

:wait_for_state
set "State=Wait"
set "LoopCount=60"
:Retry
for /F "tokens=*" %%J in ('VBoxManage guestproperty get "%VM_NAME%" "State"') do set "STATE=%%J"
if not "%State:Finished=%" == "%State%" exit /B 0
set /A LoopCount-=1
if %LoopCount% == 0 exit /B 1
%WaitASecond% >nul
goto Retry

The code expects that the execution of the following command line in background by FOR inside the subroutine wait_for_state finishes always quickly in a few milliseconds:

C:\Windows\System32\cmd.exe /c VBoxManage guestproperty get "Win10-Pro-x32" "State"

The command FOR always waits for self-termination of started cmd.exe which closes itself after execution of VBoxManage (executable or script). Then FOR processes the captured output written to the handle STDOUT (standard output) of the background command process by VBoxManage.

The IF condition if not "%State:Finished=%" == "%State%" exit /B 0 replaces in string assigned to the environment variable State case-insensitive all occurrences of the word Finished by an empty string (remove that word) and compares the resulting string with the unmodified string assigned to the environment variable State. The two compared strings are only not equal if the string assigned to the environment variable State contains case-insensitive indeed at least once the word Finished in which case the subroutine is exited with value 0 to continue the processing of the batch file below the command line call :wait_for_state.

The batch file was tested by me by creating in current directory a second batch file with name VBoxManage.cmd containing just the following command line:

@if exist test.txt del test.txt & echo ... Finished ...

I opened a second command prompt window while the batch file above was running and executed with current directory being the directory containing the two batch files from time to time the command echo Test>Test.txt to test both exit conditions of the subroutine.

To understand the commands used and how they work, open a command prompt window, execute there the following commands, and read the displayed help pages for each command, entirely and carefully.

  • call /?
  • echo /?
  • endlocal /?
  • exit /?
  • goto /?
  • if /?
  • set /?
  • setlocal /?

Upvotes: 2

Related Questions