nico
nico

Reputation: 1332

BATCH — Store command output to variable

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

Answers (2)

Mofi
Mofi

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

Compo
Compo

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

Related Questions