Batch if statements based on opening a program - windows

Is it possible to make an if statement for batch based off a user opening a program?
I tried this:
color c
echo off
cls
:props
if open WINWORD.exe (echo a & pause 10 & exit)
goto props
Doing so will simply post the error 'WINWORD was unexpected at this time.' and kill the command prompt.
What I am trying to achieve is a batch file that will:
Look if anyone is opening WINWORD.exe
Once somebody has opened the file the command prompt will display 'a'
Exit the command prompt after 10 seconds once 'a' was displayed.

You can search for the process using tasklist.exe, together with find to count the instances:
set PROC=winword.exe
tasklist /FI "IMAGENAME eq %PROC%" | find /C /I "%PROC%"
I used a variable %PROC% to specify the process (*.exe) name just for the sake of convenience.
find will also exit with the return code (ErrorLevel) of 1 if no instances have been found. With a simple if statement you can do what you requested:
if not ErrorLevel 1 ((echo a) & timeout /T 10 /NOBREAK & exit)
I replaced the pause 10 by the timeout command because pause waits for the user to press any key (any arguments are ignored). The switch /NOBREAK means to ignore any key presses.
The parenthesis around echo a avoids a trailing space to be echoed as well.
So all together, the following should do what you asked for:
color c
echo off
set PROC=winword.exe
cls
:props
tasklist /FI "IMAGENAME eq %PROC%" | find /C /I "%PROC%" > nul
if not ErrorLevel 1 ((echo a) & timeout /T 10 /NOBREAK & exit)
timeout /T 1 /NOBREAK > nul
goto props
The (optional) > nul portion after find prevents it from displaying the found number of instances.
The second delay time timeout /T 1 has been inserted to avoid massive CPU load within this loop structure.

Related

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;
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.
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.
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 ??:
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

How would I check if a named program is running in a batch script?

For some reason it says awesome even when the program is not open, and even if I put in a window name like "asdfsd" or something random. Can anyone help?
#echo off
:start
tasklist | find /I "WINDOWNAME"
if errorlevel 1 (
echo awesome
)
goto :start
At first, let me recommend not to use find just to find a certain window title in the whole output of tasklist, because the search string might occur somewhere else, like the image name, for example, which could lead to false matches.
Anyway, the tasklist command does not set the exit code (ErrorLevel) when the filter /FI does not find a match, but you could check whether the output begins with INFO:, which is the case when no match was encountered:
:start
timeout /T 1
tasklist /FI "WindowTitle eq WindowName" | findstr /B "INFO:" > nul && (echo goto :start) || (echo awesome)`.
This depends on the returned single line in case of no matches:
INFO: No tasks are running which match the specified criteria.
This text depends on the locale/region/language settings of the system. To make this even locale-independent, you could use the a trick: tasklist, with default output format (/FO TABLE), returns more than a single line when at least a match is encountered, because there is a two-line header followed by the actual matching items; if there is no match, the aforementioned line is the only one returned. So capture the output of tasklist by a for /F loop, using the option skip=1. The for /F loop will then set the exit code to 1 (not the ErrorLevel though) when it does not iterate, and to 0 when it iterates at least once. This exit code can be checked using the conditional execution operators && and ||:
:start
timeout /T 1
(for /F "skip=1" %%I in ('tasklist /FI "WindowTitle eq WindowName"') do rem/) && (echo awesome) || (goto :start)
I inserted the timeout command in order to avoid heavy CPU loads by the goto :start loop.

"IF ERRORLEVEL" chain sets wrong value

I am trying to use the choice command in a batch script to take the default input if the user doesn't want to stop kicking off a report. I wrote the below script but instead of waiting 10 seconds and kicking off the report, it is recursively echo-ing the first line of the code over and over until I kill the script. Is there something wrong that I am doing?
My Code:
CHOICE /C YN /N /T 10 /D N /M "Run Report Y or N?"
IF ERRORLEVEL 1 SET REPORT=RunTheReport:
IF ERRORLEVEL 2 SET REPORT=DontRunIt:
ECHO You chose to run %REPORT%
P.S: I replaced the report commands with an echo statement but it is still not working
You have found one of the few instances where the difference between .cmd and .bat is important.
The sample you posted works correctly, when you save that code to a file named script.bat.
script.bat
CHOICE /C YN /N /T 10 /D N /M "Run Report Y or N?"
IF ERRORLEVEL 1 SET REPORT=RunTheReport
IF ERRORLEVEL 2 SET REPORT=DontRunIt
ECHO You chose to run %REPORT%
When the user presses Y the errorlevel is set to 1. The first IF line matches and sets REPORT=RunTheReport. The second IF line does not match, and the end result is Run.
When the user presses N the errorlevel is set to 2. The first IF line matches and sets REPORT=RunTheReport. The second IF line matches and sets REPORT=DontRunIt. The end result is Don't Run.
In a .bat file, the IF ERRORLEVEL ladder will continue and execute every matching SET line. The last matching SET will be the one used.
If you save that same code to a file named script.cmd CMD behaves a little bit differently. One difference is that the SET command now sets ERRORLEVEL to 0 when it successfully sets a variable.
When the user presses Y the errorlevel is set to 1. The first IF line matches and sets REPORT=RunTheReport. The second IF line does not match, and the end result is Run, just like the .bat case.
When the user presses N the errorlevel is set to 2. The first IF line matches, sets REPORT=RunTheReport, and sets ERRORLEVEL to 0. The second IF line then does NOT match. The end result is also Run, which is wrong.
In a .cmd file, only the first matching SET line is run.
Therefore, if your file is named with a .cmd extension, you must reverse the order of the IF ERRORLEVEL lines, so that the correct one is the only one executed.
script.cmd
CHOICE /C YN /N /T 10 /D N /M "Run Report Y or N?"
IF ERRORLEVEL 2 SET REPORT=DontRunIt
IF ERRORLEVEL 1 SET REPORT=RunTheReport
ECHO You chose to run %REPORT%
There is an easy way to avoid this issue and make your code work in both types of file. The IF ERRORLEVEL N syntax is deprecated. It is confusing because it matches as greater-than-or-equal, rather than equal. Instead, use the newer IF %errorlevel% EQU N syntax.
script2.{bat|cmd}
CHOICE /C YN /N /T 10 /D N /M "Run Report Y or N?"
IF %ERRORLEVEL% EQU 1 SET REPORT=RunTheReport
IF %ERRORLEVEL% EQU 2 SET REPORT=DontRunIt
ECHO You chose to run %REPORT%
Save as test.bat and run this script. It works well. Edited to show different approaches possible. It runs a bit faster:
#echo off
CHOICE /C YN /N /T 10 /D N /M "Run Report Y or N?"
IF "%errorlevel%"=="2" (SET "REPORT=DontRunIt:"
) else (SET "REPORT=RunTheReport:")
ECHO You chose to run %REPORT%
timeout 5
exit /b

Batch - Kill program if running, start it if it's not

I'm trying to make a toggle batch script for a process so that if it's running it gets killed and if it's not it is started. This is what I have:
tasklist /FI "IMAGENAME eq ProcessName.exe" 2>NUL | find /I /N "ProcessName.exe">NUL
set procopen = %ERRORLEVEL%
if "%procopen%"=="0" taskkill /im ProcessName.exe
if NOT "%procopen%"=="0" start "" ProcessName.exe
But everytime I run it, after the first if statement I receive the error:
"1"=="0") was unexpected at this time.
I also feel like there's a more efficient way to write this, but I'm not exactly sure how. Any help is appreciated!
More efficient would be to use conditional execution.
tasklist | find /i "application.exe" >NUL && (
taskkill /im "application.exe" /f
) || (
start "" application.exe
)
I think the reason your script is failing is because you've got spaces surrounding your equal sign in your set procopen line. You're basically setting a variable named procopenspace=spacenumeral, with the spaces included in both the variable name and the value. In the set command, spaces are treated as literal space characters, rather than as token delimiters. Now if you had done set /a, it probably would've worked (as set /a is more tolerant of spacing). Or if you had left the spaces out and set "procopen=%ERRORLEVEL%", that probably would've worked too. Here's an example cmd console session to demonstrate:
C:\Users\me>set procopen = 0
C:\Users\me>set "res=%procopen%"
C:\Users\me>set res
res=%procopen%
C:\Users\me>set "res=%procopen %"
C:\Users\me>set res
res= 0

Batch files can not supress "terminate job"

Im trying to open a 2nd batch file and detect if it normally exited or closed by a user (ctrl+c or x or window termiate etc..)
so Im using this following example by Batch run script when closed
#Echo off
set errorlevel=1
start /w %comspec% /c "mode 70,10&title Folder Confirmation Box&color 1e&echo.&echo. Else the close window&pause>NUL&exit 12345"
echo %errorlevel%
pause
Im trying to keep 1st batch waiting (/W) since I will check for errorlevel later on
But after closing the 2nd batch file I get an error like ^cterminate batch job (Y/N)?
I tried the suggestion over https://superuser.com/questions/35698/how-to-supress-terminate-batch-job-y-n-confirmation
with the script
rem Bypass "Terminate Batch Job" prompt.
if "%~2"=="-FIXED_CTRL_C" (
REM Remove the -FIXED_CTRL_C parameter
SHIFT
) ELSE (
REM Run the batch with <NUL and -FIXED_CTRL_C
CALL <NUL %1 -FIXED_CTRL_C %*
GOTO :EOF
)
That works quite fine
So is there a way of starting from same batch file and avoiding the terminating?
Or do I have to create a new batch from same batch and call it?
(I don't want them to see the file aswell)
Do not assign values to a volatile environment variable like errorlevel using set command. Doing that causes it becomes unvolatile in current context.
Always use title in START "title" [/D path] [options] "command" [parameters].
start "" /W cmd /c "anycommand&exit /B 12345" always returns 12345 exit code. It's because all the cmd line with & concatenated commands is prepared in parsing time (the same as a command block enclosed in parentheses) and then run entirely, indivisibly. Omit &exit /B 12345 to get proper exit code from anycommand, or replace it with something like start "" /W cmd /c "anycommand&&exit /B 12345||exit /B 54321" to get only success/failure indication.
Next code snippet could help:
#ECHO OFF
SETLOCAL enableextensions
set "_command=2nd_batch_file.bat"
:: for debugging purposes
set "_command=TIMEOUT /T 10 /NOBREAK"
:: raise errorlevel 9009 as a valid file name can't contain a vertical line
invalid^|command>nul 2>&1
echo before %errorlevel%
start "" /w %comspec% /C "mode 70,10&title Folder Confirmation Box&color 1e&echo(&echo( Else the close window&%_command%"
echo after %errorlevel%
Output shows sample %_command% exit codes: 0 or 1 if came to an end properly but -1073741510 if terminated forceably by Ctrl+C or Ctrl+Break or red ×
==>D:\bat\SO\31866091.bat<nul
before 9009
after 0
==>D:\bat\SO\31866091.bat<nul
before 9009
after 1
==>D:\bat\SO\31866091.bat<nul
before 9009
^CTerminate batch job (Y/N)?
after -1073741510
==>
This works for me:
call :runme start /w "Child Process" %comspec% /c "child.bat & exit 12345" <NUL >NUL 2>NUL
echo %ERRORLEVEL%
goto :eof
:runme
%*
goto :eof
The idea is to call a subroutine in the current script rather than calling out to an external script. You can still redirect input and output for a subroutine call.

Resources