Reputation: 1332
I would like to make a bat script that performs an action only if fewer than 2 occurrences of cmd.exe are running. I managed to find a solution that stores the occurrence count in a temporary file, but I find it very inelegant. So my question is how to do the same as below without a temporary file, but rather by storing the occurence count given by @TASKLIST | FIND /I /C "%_process%"
in a variable. I saw that there may be a solution with a for loop, but I couldn’t get it to work and anyway I would really prefer a solution based on SET
(if this is possible).
@SET _process=cmd.exe
@SET _temp_file=tempfiletodelete.txt
@TASKLIST | FIND /I /C "%_process%" > %_temp_file%
@SET /p _count=<%_temp_file%
@DEL /f %_temp_file%
@IF %_count% LSS 2 (
@ECHO action 1
) ELSE (
@ECHO action 2
)
Edit: This question is similar to Save output from FIND command to variable, but I wasn’t able to apply the solution to my problem and I wanted to know if a solution without a for loop is possible.
Upvotes: 5
Views: 25006
Reputation: 49086
The command FOR with option /F
and the command or command line specified as set (string inside parentheses) additionally enclosed by '
can be used for processing the output of a command or a command line with multiple commands on one line.
You can use this batch file:
@ECHO OFF
SET "_process=cmd.exe"
FOR /F %%I IN ('%SystemRoot%\System32\tasklist.exe /FI "IMAGENAME eq %_process%" ^| %SystemRoot%\System32\find.exe /I /C "%_process%"') DO SET "_count=%%I"
IF /I "%_process%" == "cmd.exe" SET /A _count-=1
IF %_count% LSS 2 (
ECHO action 1
) ELSE (
ECHO action 2
)
The command FOR runs in background without a visible console window cmd.exe /C
with the command line:
C:\Windows\System32\tasklist.exe /FI "IMAGENAME eq cmd.exe" | C:\Windows\System32\find.exe /I /C "cmd.exe"
TASKLIST option /FI "IMAGENAME eq cmd.exe"
filters the output of TASKLIST already to processes with image name (= name of executable) cmd.exe
. This makes further processing faster and avoids that a running process with file name totalcmd.exe
is counted as cmd.exe
as the command line in question does.
The output of TASKLIST written to handle STDOUT is redirected to handle STDIN of command FIND which processes the lines and counts the lines containing cmd.exe
anywhere in line. FIND outputs finally the number of lines containing searched string to handle STDOUT of command process running in background.
The redirection operator |
must be escaped with caret character ^
on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded command line with using a separate command process started in background.
FOR with option /F
captures everything written to handle STDOUT of executed command process and processes each line. In this case just one line is captured by FOR containing just a number. Therefore no additional options are needed to assign the number assigned to loop variable I
by FOR to environment variable _count
using command SET.
The goal of this batch file is counting the number of cmd.exe
processes already running. As the command line with TASKLIST and FIND is executed by FOR in background with using one more cmd.exe
process, it is necessary to subtract the count by one to get the correct result using an arithmetic expression evaluated with SET /A _count-=1
. This decrement by one is needed only for counting right the number of cmd.exe
processes. It is not necessary for any other process.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
find /?
for /?
if /?
set /?
tasklist /?
Upvotes: 6
Reputation: 38579
You would do it like this, using TaskList
in a For
loop:
@Set "_process=cmd.exe"
@For /F %%A In ('TaskList^|Find /C /I "%_process%"'
) Do @If %%A Lss 2 (Echo Action 1) Else Echo Action 2
@Pause
You can also perform a similar thing using WMIC
too:
@Set "_process=cmd.exe"
@For /F %%A In ('WMIC Process Get Name^|Find /C "%_process%"'
) Do @If %%A Lss 2 (Echo Action 1) Else Echo Action 2
@Pause
These could be refined further to ensure processes containing the string as opposed to matching it aren't counted:
@Set "_process=cmd.exe"
@For /F %%A In ('TaskList /FI "ImageName Eq "%_Process%""^|Find /C "."'
) Do @If %%A Lss 2 (Echo Action 1) Else Echo Action 2
@Pause
@Set "_process=cmd.exe"
@For /F %%A In (
'WMIC Process Where "Name='%_process%'" Get Name^|Find /C "."'
) Do @If %%A Lss 2 (Echo Action 1) Else Echo Action 2
@Pause
Note:
If you're really checking the cmd.exe
process, be aware that an the command inside the For
loop parentheses is spawned within a new cmd.exe
instance, (thereby increasing your count by 1
)
Upvotes: 5