user12261902
user12261902

Reputation:

Display Timer while a command is being executed in a batch file

Edit: I shall thank Magoo for the detailed answer. I edited my batch file according to Magoo's answer. Here is the new code:

@echo off
mode 62,22
:Begin
choice /c 12345 /M "Insert a number (min. 1 - max. 5): "
set "CommandVar0=%errorlevel%"
choice /c 12345 /M "Please confirm the number you entered: "
set "CommandVarC=%errorlevel%"

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

if %CommandVar0% == 1 set /P CommandVar1=Insert ID: 
if %CommandVar0% == 2 set /P CommandVar1=Insert ID: & set /P CommandVar2=Insert ID: 
if %CommandVar0% == 3 set /P CommandVar1=Insert ID: & set /P CommandVar2=Insert ID: & set /P CommandVar3=Insert ID: 
if %CommandVar0% == 4 set /P CommandVar1=Insert ID: & set /P CommandVar2=Insert ID: & set /P CommandVar3=Insert ID: & set /P CommandVar4=Insert ID: 
if %CommandVar0% == 5 set /P CommandVar1=Insert ID: & set /P CommandVar2=Insert ID: & set /P CommandVar3=Insert ID: & set /P CommandVar4=Insert ID: & set /P CommandVar5=Insert ID: 

:loop
start EXAMPLE.exe %CommandVar1%
if defined commandvar2 start EXAMPLE.exe %CommandVar2%
if defined commandvar3 start EXAMPLE.exe %CommandVar3%
if defined commandvar4 start EXAMPLE.exe %CommandVar4%
if defined commandvar5 start EXAMPLE.exe %CommandVar5%
rem wait 10 seconds
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
)
taskkill /f /im EXAMPLE.exe
timeout /t 5 
goto loop

The issue I'm having right now is that;

  1. Due to my lack of knowledge, I don't know how to expand this command:
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
)

e.g. to set "commandvar5=" etc. So instead I'm still using my complicated command which does the job for now.

  1. How can I add the normal timer to this command?
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
)

I want a timer to actually display the countdown of the command being executed like the usual timeout /t command if possible.

Upvotes: 0

Views: 493

Answers (2)

Magoo
Magoo

Reputation: 79982

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...

--- To add Commandvars ---

The method is limited to 9 items without complications.

Change the 12 in the choice commands to 123456789 (or whatever your limit is).

add

for /L %%e in (1,1,9) do set "commandvar%%e="

in place of set "commandvar1=" set "commandvar2="

This will set all variablenames commandvar1..9 to nothing.

Then

for /L %%e in (1,1,9) do (
 set /P CommandVar%%e="Insert ID for instance %%e: "
 if not defined commandvar%%e (
  if %%e==1 goto begin
  goto startprocs
 )
)
:startprocs
for /L %%e in (1,1,9) do if defined commandvar%%e call start "Instance %%e" u:\dummy.bat %%commandvar%%e%%

Note that the set/p has a quoted prompt-string. This allows the string to have a terminal space. It's possible to have that string "nude" but if your editor gets over-enthusiastic and removes terminal spaces, then you'd lose that one. It also serves to make the space obvious (stray spaces can cause havoc in batch)

Then input your data. If the first entry is empty, back to the start. You could change that goto to an exit, which would terminate the cmd instance. or exit /b which would terminate this batch, but keep your cmd session open.

If any other entry is empty, it's the signal to start processing as we've finished our list.

The for /L for starting the processes has been changed a little. First, the start is immediately followed by a quoted string. This is the title which would appear in the process's window. It could be empty if you wish, but it should not be omitted in the general case. Start regards the first quoted string in the command as the window title to be used, so if it is omitted, a quoted string in the command may be eaten by start, used as a title and not passed to the process being started.

Then there's the call. This invokes a parser trick. Suppose %%e is 2. The command would be executed as call start "Instance 2" u:\dummy.bat %%commandvar%%e%% where %%commandvar%%e%% would be interpreted left-to-right as %commandvar2% as % is the "escape character" for %. Escaping a character is where we want to use a character without its special meaning. %%e is replaced by 2 first because it's an active metavariable (loop-control character or parameter-number). CALLin the resultant command start "Instance 2" u:\dummy.bat %commandvar2% then substitutes the current value of commandvar2 for the process.

=== Summary thought ===

With this new approach to have multiple commandvaar entries, there appears to be no reason for the nomination of a number of entries and then checking that number, so that entire section could be regarded as redundant.

If you remove that section, then there's no reason for the restriction to 9 items. You could set the limit to any number you like by replacing the 9 with 22, 35, 957 - whatever takes your fancy. The process will now start whenever you reply Enter to a prompt Insert ID for instance ??:

Upvotes: 3

DSalomon
DSalomon

Reputation: 49

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 

Upvotes: -1

Related Questions