Home > Blockchain >  Return if statement in batch script to loop
Return if statement in batch script to loop

Time:11-26

I'm not a programmer so please bear with me.

This is my batch file:

@echo off
:Begin
set /P CommandVar0=Insert a number (min. 1 - max. 2): 
set /P CommandVarC=Please confirm the number you entered: 
if %CommandVarC% NEQ %CommandVar0% (echo Insert a matching number between 1~2 for confirmation.
goto Begin
) else if %CommandVarC% GTR 2 (echo Maximum number is 2.
goto Begin
) else (
if %CommandVar0% == 1 set /P CommandVar1=Insert ID: 
if %CommandVar0% == 2 set /P CommandVar1=Insert ID: & set /P CommandVar2=Insert ID: 
)


// Failed Method 1
:loopF
start EXAMPLE.exe %CommandVar1% & start EXAMPLE.exe %CommandVar2%
tasklist /fi "imagename eq EXAMPLE.exe" |find ":" > nul
if errorlevel 1 (echo Not found.
goto Begin
) else (
:loop
start EXAMPLE.exe %CommandVar1% & start EXAMPLE.exe %CommandVar2%
timeout /t 10
taskkill /f /im EXAMPLE.exe
timeout /t 5
goto loop
)


// Failed Method 2
:loop
start EXAMPLE.exe %CommandVar1% & start EXAMPLE.exe %CommandVar2%
tasklist /fi "imagename eq EXAMPLE.exe" |find ":" > nul
if errorlevel 1 (echo Not found.
goto Begin
) else (
timeout /t 10
taskkill /f /im EXAMPLE.exe
timeout /t 5
goto loop
)


// Failed Method 3
:loop
start EXAMPLE.exe %CommandVar1% & start EXAMPLE.exe %CommandVar2%
tasklist /fi "imagename eq EXAMPLE.exe" |find ":" > nul
if errorlevel 1 (echo Not found.
goto Begin
) else if errorlevel 0 (echo Found.
timeout /t 10 
taskkill /f /im EXAMPLE.exe
timeout /t 5 
goto loop
)

I want the command to return to the loop if the program is running and return to Begin if it's not running. This part of the batch file does not work. It just works until the "goto Begin" part. What am I doing wrong?

timeout /t 10
taskkill /f /im EXAMPLE.exe
timeout /t 5
goto loop

Tried 3 methods with else and if else.

CodePudding user response:

Addressing the core of the problem:

start EXAMPLE.exe %CommandVar1% & start EXAMPLE.exe %CommandVar2%

Will start two instances of example.exe passing the contents of commandvar? which may be response to "Insert ID:" or (rarely) nothing

Immediately thereafter, you are executing tasklist and looking for :.

If either the tasks have actually been established and is running, the tasklist output will NOT contain : so find will NOT find : and hence set errorlevel to 1

If the tasks have not been started or have both terminated, then the output of tasklist would be INFO: No tasks are running which match the specified criteria. which contains : and hence errorlevel will be set to 0.

Your next line says if **either** task is running (errorlevel 1), say "not found" and go to Begin otherwise (ie. neither task is running) then delay, taskkill the non-existent tasks, delay again and go back to Begin.

Another hidden problem is that when the routine returns to begin, if CommandVar2 has been set, then it will not be cleared, so it will remain set for the next input-cycle, regardless of the data input.

So we need to tackle the logic problems as well as the batch-syntax-specific problems.

Tip: Use set "var=value" for setting string values - this avoids problems caused by trailing spaces. Don't assign " or a terminal backslash or Space. Build pathnames from the elements - counterintuitively, it is likely to make the process easier. If the syntax set var="value" is used, then the quotes become part of the value assigned.

First thing to tackle is the choose-and-check part. The value assigned to a variable by a set /p can be any string. That string may be the expected string or an unexpected string (obviously). An unexpected string may take many forms - it might contain spaces for instance, or unbalanced quotes, or % or parentheses or other strings or characters that have significance in the batch language. Also, responding with Enter to a set /p will leave the variable unchanged, not set it to nothing.

When the obvious parameter-check is executed via if %var1% == %var2% ..., then the values of the variables are substituted, and the comparison executed, so had the user input break me for instance, then batch would attempt to execute if break me == 2 ... and batch would see me where it expects to see a comparison-operator like ==.

The usual way to by-pass this problem is by "quoting both sides", ie. if "%var1%" == "%var2%" ... This is useful in the case that you may be processing some string containing spaces that is being returned from a command, but what happens if the user enters break " me ?

Consequently, the recommendation is to use the choice command to make 1-character choices (see choice /? from the prompt for documentation, or thousands of items on SO for examples)

Hence, your code should be

choice /c 12 /M "Insert a number (min. 1 - max. 2): "
set "CommandVar0=%errorlevel%"
choice /c 12 /M "Please confirm the number you entered: "
set "CommandVarC=%errorlevel%"

At this point, we are sure that commandvar? contains 1 or 2, so we can indeed use

if %CommandVarC% NEQ %CommandVar0% (
 echo Insert a matching number between 1~2 for confirmation
 goto Begin
) 

Note: I've used the string-assignment syntax, even though the value assigned from %errorlevel% must be a pure-numeric string. It's also possible to use set /a var=%errorlevel% here. set /a assigns a value that we know is numeric (and can also do calculations) and needs no quotes.

There's no point in else... here -it serves to complicate matters. The code will simply continue to the next line if the goto is not executed.

 if %CommandVarC% GTR 2 (echo Maximum number is 2.
    goto Begin
    ) else (

is not required. We know that CommandVarC can only be 1 or 2.

if %CommandVar0% == 1 set /P CommandVar1=Insert ID: 
if %CommandVar0% == 2 set /P CommandVar1=Insert ID: & set /P CommandVar2=Insert ID: 

Well, this is overcomplicated, and doesn't quite do what is required. CommandVar1 is always required, and CommandVar2 will remain set if it has been set in the past.

Try:

set "commandvar1="
set "commandvar2="
set /P CommandVar1=Insert ID: 
if not defined commandvar1 goto begin
if %CommandVar0% == 2 set /P CommandVar2=Insert ID: 

Well, primitively. We know commandvar0 is a single digit, so we can use a simple ==. We also may or may not have received a response for commandvar2.

So

if "%CommandVar0%"=="2" (
 set /P CommandVar2=Insert ID: 
 if not defined commandvar2 goto begin
)

may be more "according to Hoyle"

Summing up (before we tackle the next part), try using

choice /c 12 /M "Insert a number (min. 1 - max. 2): "
set "CommandVar0=%errorlevel%"
choice /c 12 /M "Please confirm the number you entered: "
set "CommandVarC=%errorlevel%"

if %CommandVarC% NEQ %CommandVar0% (
 echo Insert a matching number between 1~2 for confirmation
 goto Begin
) 

set "commandvar1="
set "commandvar2="
set /P CommandVar1=Insert ID: 
if not defined commandvar1 goto begin
if "%CommandVar0%"=="2" (
 set /P CommandVar2=Insert ID: 
 if not defined commandvar2 goto begin
)

If we get past this point, I gather we should launch the executable, possibly twice, then wait for it to run, with a run-time limit of 10 seconds.

So,

start EXAMPLE.exe %CommandVar1%
rem only start a second instance if commandvar2 has been specified
if defined commandvar2 start EXAMPLE.exe %CommandVar2%
rem wait a sec...or 10
for /L %%y in (1,1,10) do (
 timeout /t 1 >nul
 tasklist /fi "imagename eq EXAMPLE.exe" |find ":" > nul
 if NOT errorlevel 1 goto begin
)
rem kill tasks still running
taskkill /f /im EXAMPLE.exe
timeout /t 5 
goto begin

for /L is explained at for /? from the prompt. Here, it counts %%y from 1 to 10 in steps of 1.

Each step delays 1 second (the >nul suppresses the countdown) then tests whether an executable is running. If NONE is running, then the tasklist will report a line containing : and errorlevel will be set to 0.

IF ERRORLEVEL n is TRUE if the runtime (ie. current) errorlevel is n or greater than n. IF ERRORLEVEL 0 is therefore always true. IF NOT ERRORLEVEL 1 is a test for errorlevel=0.

So, if there is no executable running, goto begin.

Once this test has been done 10 times, if an executable is running, then we exit the for /L loop and taskkill the remaining task(s) and back to the future...

CodePudding user response:

Parham.8, the error level is set after each command has been executed, if the next command succeeds, the error level will take 0 independent of the previous one. Use && to execute a command only if the previous command's error level is 0. Ex.

start EXAMPLE.exe %CommandVar1% && start EXAMPLE.exe %CommandVar2% && tasklist /fi "imagename eq EXAMPLE.exe" |find ":" > nul 
  • Related