Matt Lacey
Matt Lacey

Reputation: 65586

How to check if a process is running via a batch script

How can I check if an application is running from a batch (well cmd) file?

I need to not launch another instance if a program is already running. (I can't change the app to make it single instance only.)

Also the application could be running as any user.

Upvotes: 311

Views: 801198

Answers (22)

AlainD
AlainD

Reputation: 6635

To count the number of instances of the running process:

set "ProcessName=Notepad.exe"
set /a CountProcesses = 0
for /f "tokens=2 delims=:" %%a in ('tasklist /V /FI "imagename EQ %ProcessName%" /FO LIST ^| FIND "PID"') do set /a CountProcesses+=1
echo %CountProcesses% '%ProcessName%' processes are currently running

Adapted from the accepted answer from @Chaosmaster.

Update

Based on the comment from @Mofi, the following also works:

...
set "CountProcesses=0"
for /F %%I in ('tasklist /NH /FI "imagename EQ %ProcessName%" ^| find /C /I "%ProcessName%"') do set "CountProcesses=%%I"
...

Nice idea, but IMO set /a is to be preferred because the intention of generating a numerical answer is more explicit. The CountProcesses result (as a number) can then be used more naturally in equations using LSS, GTR and other numerical operators.

Upvotes: 0

piers7
piers7

Reputation: 4414

Simplified version based on Chaosmaster's answer plus Riccardo La Marca's answer

tasklist /FI "ImageName eq notepad.exe" /fo list | find /i "Image Name:" > nul || start notepad.exe
  • Removes the duplicate filtering (keeps the filtering in tasklist, as less chance of 'included in' false positives). Find just detects the header from a match.
  • Uses || to just run the 'it's not running' command directly, rather than IF ERRROLEVEL shenanigans

Upvotes: 0

Willy
Willy

Reputation: 10650

Using tasklist with default standard output for results makes the image name to be cut so when applying the find /I it is not finding it.

So to avoid tasklist to cut long names under image name column, change the output to be a list and then search for your full app name there.

See below example:

@echo off
set "fullProcessName=myveryveryveryveryverylongappnameeeee.exe"

:: Check if the process is running and capture the output
:: Note we use /FO LIST
tasklist /FI "IMAGENAME eq %fullProcessName%" /FO LIST 2>NUL | find /I "%fullProcessName%" >NUL
if %errorlevel% equ 0 (
    echo The process %fullProcessName% is running.
) else (
    echo The process %fullProcessName% is not running.
)

Upvotes: 0

TrueY
TrueY

Reputation: 7610

I like Chaosmaster's solution! But I looked for a solution which does not start another external program (like find.exe or findstr.exe). So I added the idea from Matt Lacey's solution, which creates an also avoidable temp file. At the end I could find a fairly simple solution, so I share it...

SETLOCAL EnableExtensions
set EXE=MyProg.exe
FOR /F %%x IN ('tasklist /NH /FI "IMAGENAME eq %EXE%"') DO IF NOT %%x == %EXE% (
  echo %EXE% is Not Running
)

This is working for me nicely...

The above is an edit. The original code apparently had a GOTO in it, which someone in the comments thought uncouth.

Spaces

If you are concerned that the program name may have spaces in it then you need to complicate the code very slightly:

SETLOCAL EnableExtensions
set EXE=My Prog.exe
FOR /F %%x IN ("%EXE%") do set EXE_=%%x
FOR /F %%x IN ('tasklist /NH /FI "IMAGENAME eq %EXE%"') DO IF NOT %%x == %EXE_% (
  echo %EXE% is Not Running
)

The original code will work fine whether or not other running processes have spaces in their names. The only concern is whether or not the process we are targeting has space(s).

ELSE

Keep in mind that if you add an ELSE clause then it will be executed once for every instance of the application that is already running. There is no guarantee that there be only a single instance running when you run this script.

Should you want one anyway, either a GOTO or a flag variable is indicated.

Ideally the targeted application should already mutex itself to prevent multiple instances, but that is a topic for another SO question and is not necessarily applicable to the subject of this question.

GOTO again

I do agree with the "ELSE" comment. The problem with the GOTO-less solution, that is may run the condition part (and the ELSE part) multiple times, so it is a bit messy as it has to quit the loop anyway. (Sorry, but I do not deal with the SPACE issue here, as it seems to be pretty rare and a solution is shown for it)

SETLOCAL EnableExtensions
SET EXE=MyProg.exe
REM for testing
REM SET EXE=svchost.exe
FOR /F %%x IN ('tasklist /NH /FI "IMAGENAME eq %EXE%"') DO IF NOT %%x == %EXE% (
  ECHO %EXE% is Not Running
  REM This GOTO may be not necessary
  GOTO notRunning
) ELSE (
  ECHO %EXE is running
  GOTO Running
)
...
:Running
REM If Running label not exists, it will loop over all found tasks

Upvotes: 58

arnold_w
arnold_w

Reputation: 69

If you have more than one .exe-file with the same name and you only want to check one of them (e.g. you care about C:\MyProject\bin\release\MyApplication.exe but not C:\MyProject\bin\debug\MyApplication.exe) then you can use the following:

@echo off

set "workdir=C:\MyProject\bin\release"
set "workdir=%workdir:\=\\%"

setlocal enableDelayedExpansion
for /f "usebackq tokens=* delims=" %%a in (`
    wmic process  where 'CommandLine like "%%!workdir!%%" and not CommandLine like "%%RuntimeBroker%%"'   get CommandLine^,ProcessId  /format:value
`) do (
    for /f "tokens=* delims=" %%G in ("%%a")  do (
        if "%%G" neq "" (
rem            echo %%G
            set "%%G"
rem            echo !ProcessId!
            goto :TheApplicationIsRunning
        )
    )
) 

echo The application is not running
exit /B

:TheApplicationIsRunning
echo The application is running
exit /B

Upvotes: 2

Chaosmaster
Chaosmaster

Reputation:

Another possibility I came up with, which does not require to save a file, inspired by using grep is:

tasklist /fi "ImageName eq MyApp.exe" /fo csv 2>NUL | find /I "myapp.exe">NUL
if "%ERRORLEVEL%"=="0" echo Program is running
  • /fi "" defines a filter of apps to find, in our case it's the *.exe name
  • /fo csv defines the output format, csv is required because by default the name of the executable may be truncated if it is too long and thus wouldn't be matched by find later.
  • find /I means case-insensitive matching and may be omitted

See the man page of the tasklist command for the whole syntax.

Upvotes: 393

Patrick
Patrick

Reputation: 97

I needed a solution with a retry. This code will run until the process is found and then kill it. You can set a timeout or anything if you like.

Notes:

  • The ".exe" is mandatory
  • You could make a file runnable with parameters, version below
    :: Set programm you want to kill
    :: Fileextension is mandatory
    SET KillProg=explorer.exe

    :: Set waiting time between 2 requests in seconds
    SET /A "_wait=3"

    :ProcessNotFound
        tasklist /NH /FI "IMAGENAME eq %KillProg%" | FIND /I "%KillProg%"
        IF "%ERRORLEVEL%"=="0" (
            TASKKILL.EXE /F /T /IM %KillProg%
        ) ELSE (
            timeout /t %_wait%
            GOTO :ProcessNotFound
        )

taskkill.bat:

    :: Get program name from argumentlist
    IF NOT "%~1"=="" (
        SET "KillProg=%~1"
    ) ELSE (
        ECHO Usage: "%~nx0" ProgramToKill.exe & EXIT /B
    )

    :: Set waiting time between 2 requests in seconds
    SET /A "_wait=3"

    :ProcessNotFound
        tasklist /NH /FI "IMAGENAME eq %KillProg%" | FIND /I "%KillProg%"
        IF "%ERRORLEVEL%"=="0" (
            TASKKILL.EXE /F /T /IM %KillProg%
        ) ELSE (
            timeout /t %_wait%
            GOTO :ProcessNotFound
        )

Run with .\taskkill.bat ProgramToKill.exe

Upvotes: 1

drzaus
drzaus

Reputation: 25034

Just mentioning, if your task name is really long then it won't appear in its entirety in the tasklist result, so it might be safer (other than localization) to check for the opposite.

Variation of this answer:

:: in case your task name is really long, check for the 'opposite' and find  the message when it's not there
tasklist /fi "imagename eq yourreallylongtasknamethatwontfitinthelist.exe" 2>NUL | find /I /N "no tasks are running">NUL
if "%errorlevel%"=="0" (
    echo Task Found
) else (
    echo Not Found Task
)

Upvotes: 2

Riccardo La Marca
Riccardo La Marca

Reputation: 895

TASKLIST | FINDSTR ProgramName || START "" "Path\ProgramName.exe"

Upvotes: 32

aldemarcalazans
aldemarcalazans

Reputation: 1534

The suggestion of npocmaka to use QPROCESS instead of TASKLIST is great but, its answer is so big and complex that I feel obligated to post a quite simplified version of it which, I guess, will solve the problem of most non-advanced users:

QPROCESS "myprocess.exe">NUL
IF %ERRORLEVEL% EQU 0 ECHO "Process running"

The code above was tested in Windows 7, with a user with administrator rigths.

Upvotes: 39

Rajeev Jayaswal
Rajeev Jayaswal

Reputation: 1501

I usually execute following command in cmd prompt to check if my program.exe is running or not:

tasklist | grep program

Upvotes: -1

kayleeFrye_onDeck
kayleeFrye_onDeck

Reputation: 6978

TrueY's answer seemed the most elegant solution, however, I had to do some messing around because I didn't understand what exactly was going on. Let me clear things up to hopefully save some time for the next person.

TrueY's modified Answer:

::Change the name of notepad.exe to the process .exe that you're trying to track
::Process names are CASE SENSITIVE, so notepad.exe works but Notepad.exe does NOT
::Do not change IMAGENAME
::You can Copy and Paste this into an empty batch file and change the name of
::notepad.exe to the process you'd like to track
::Also, some large programs take a while to no longer show as not running, so
::give this batch a few seconds timer to avoid a false result!!

@echo off
SETLOCAL EnableExtensions

set EXE=notepad.exe

FOR /F %%x IN ('tasklist /NH /FI "IMAGENAME eq %EXE%"') DO IF %%x == %EXE% goto ProcessFound

goto ProcessNotFound

:ProcessFound

echo %EXE% is running
goto END
:ProcessNotFound
echo %EXE% is not running
goto END
:END
echo Finished!

Anyway, I hope that helps. I know sometimes reading batch/command-line can be kind of confusing sometimes if you're kind of a newbie, like me.

Upvotes: 12

npocmaka
npocmaka

Reputation: 57302

I like the WMIC and TASKLIST tools but they are not available in home/basic editions of windows.Another way is to use QPROCESS command available on almost every windows machine (for the ones that have terminal services - I think only win XP without SP2 , so practialy every windows machine):

@echo off
:check_process
setlocal
if "%~1" equ "" echo pass the process name as forst argument && exit /b 1
:: first argument is the process you want to check if running
set process_to_check=%~1
:: QPROCESS can display only the first 12 symbols of the running process
:: If other tool is used the line bellow could be deleted
set process_to_check=%process_to_check:~0,12%

QPROCESS * | find /i "%process_to_check%" >nul 2>&1 && (
    echo process %process_to_check%  is running
) || (
    echo process %process_to_check%  is not running
)
endlocal

QPROCESS command is not so powerful as TASKLIST and is limited in showing only 12 symbols of process name but should be taken into consideration if TASKLIST is not available.

More simple usage where it uses the name if the process as an argument (the .exe suffix is mandatory in this case where you pass the executable name):

@echo off
:check_process
setlocal
if "%~1" equ "" echo pass the process name as forst argument && exit /b 1
:: first argument is the process you want to check if running
:: .exe suffix is mandatory
set "process_to_check=%~1"


QPROCESS "%process_to_check%" >nul 2>&1 && (
    echo process %process_to_check%  is running
) || (
    echo process %process_to_check%  is not running
)
endlocal

The difference between two ways of QPROCESS usage is that the QPROCESS * will list all processes while QPROCESS some.exe will filter only the processes for the current user.

Using WMI objects through windows script host exe instead of WMIC is also an option.It should on run also on every windows machine (excluding the ones where the WSH is turned off but this is a rare case).Here bat file that lists all processes through WMI classes and can be used instead of QPROCESS in the script above (it is a jscript/bat hybrid and should be saved as .bat):

@if (@X)==(@Y) @end /* JSCRIPT COMMENT **


@echo off
cscript //E:JScript //nologo "%~f0"
exit /b

************** end of JSCRIPT COMMENT **/


var winmgmts = GetObject("winmgmts:\\\\.\\root\\cimv2");
var colProcess = winmgmts.ExecQuery("Select * from Win32_Process");
var processes =  new Enumerator(colProcess);
for (;!processes.atEnd();processes.moveNext()) {
    var process=processes.item();
    WScript.Echo( process.processID + "   " + process.Name );
}

And a modification that will check if a process is running:

@if (@X)==(@Y) @end /* JSCRIPT COMMENT **


@echo off
if "%~1" equ "" echo pass the process name as forst argument && exit /b 1
:: first argument is the process you want to check if running
set process_to_check=%~1

cscript //E:JScript //nologo "%~f0" | find /i "%process_to_check%" >nul 2>&1 && (
    echo process %process_to_check%  is running
) || (
    echo process %process_to_check%  is not running
)

exit /b

************** end of JSCRIPT COMMENT **/


var winmgmts = GetObject("winmgmts:\\\\.\\root\\cimv2");
var colProcess = winmgmts.ExecQuery("Select * from Win32_Process");
var processes =  new Enumerator(colProcess);
for (;!processes.atEnd();processes.moveNext()) {
    var process=processes.item();
    WScript.Echo( process.processID + "   " + process.Name );
}

The two options could be used on machines that have no TASKLIST.

The ultimate technique is using MSHTA . This will run on every windows machine from XP and above and does not depend on windows script host settings. the call of MSHTA could reduce a little bit the performance though (again should be saved as bat):

@if (@X)==(@Y) @end /* JSCRIPT COMMENT **
@echo off

setlocal
if "%~1" equ "" echo pass the process name as forst argument && exit /b 1
:: first argument is the process you want to check if running

set process_to_check=%~1


mshta "about:<script language='javascript' src='file://%~dpnxf0'></script>" | find /i "%process_to_check%" >nul 2>&1 && (
    echo process %process_to_check%  is running
) || (
    echo process %process_to_check%  is not running
)
endlocal
exit /b
************** end of JSCRIPT COMMENT **/


   var fso= new ActiveXObject('Scripting.FileSystemObject').GetStandardStream(1);


   var winmgmts = GetObject("winmgmts:\\\\.\\root\\cimv2");
   var colProcess = winmgmts.ExecQuery("Select * from Win32_Process");
   var processes =  new Enumerator(colProcess);
   for (;!processes.atEnd();processes.moveNext()) {
    var process=processes.item();
    fso.Write( process.processID + "   " + process.Name + "\n");
   }
   close();

Upvotes: 6

Calimo
Calimo

Reputation: 7969

Building on vtrz's answer and Samuel Renkert's answer on an other topic, I came up with the following script that only runs %EXEC_CMD% if it isn't already running:

@echo off
set EXEC_CMD="rsync.exe"
wmic process where (name=%EXEC_CMD%) get commandline | findstr /i %EXEC_CMD%> NUL
if errorlevel 1 (
    %EXEC_CMD% ...
) else (
    @echo not starting %EXEC_CMD%: already running.
)

As was said before, this requires administrative privileges.

Upvotes: 0

prakash
prakash

Reputation: 59709

You should check the parent process name, see The Code Project article about a .NET based solution**.

A non-programmatic way to check:

  1. Launch Cmd.exe
  2. Launch an application (for instance, c:\windows\notepad.exe)
  3. Check properties of the Notepad.exe process in Process Explorer
  4. Check for parent process (This shows cmd.exe)

The same can be checked by getting the parent process name.

Upvotes: -3

Matt Lacey
Matt Lacey

Reputation: 65586

Here's how I've worked it out:

tasklist /FI "IMAGENAME eq notepad.exe" /FO CSV > search.log

FOR /F %%A IN (search.log) DO IF %%~zA EQU 0 GOTO end

start notepad.exe

:end

del search.log

The above will open Notepad if it is not already running.

Edit: Note that this won't find applications hidden from the tasklist. This will include any scheduled tasks running as a different user, as these are automatically hidden.

Upvotes: 67

Nin
Nin

Reputation: 11

I used the script provided by Matt (2008-10-02). The only thing I had trouble with was that it wouldn't delete the search.log file. I expect because I had to cd to another location to start my program. I cd'd back to where the BAT file and search.log are, but it still wouldn't delete. So I resolved that by deleting the search.log file first instead of last.

del search.log

tasklist /FI "IMAGENAME eq myprog.exe" /FO CSV > search.log

FOR /F %%A IN (search.log) DO IF %%-zA EQU 0 GOTO end

cd "C:\Program Files\MyLoc\bin"

myprog.exe myuser mypwd

:end

Upvotes: 0

benmod
benmod

Reputation: 371

The answer provided by Matt Lacey works for Windows XP. However, in Windows Server 2003 the line

 tasklist /FI "IMAGENAME eq notepad.exe" /FO CSV > search.log

returns

INFO: No tasks are running which match the specified criteria.

which is then read as the process is running.

I don't have a heap of batch scripting experience, so my soulution is to then search for the process name in the search.log file and pump the results into another file and search that for any output.

tasklist /FI "IMAGENAME eq notepad.exe" /FO CSV > search.log

FINDSTR notepad.exe search.log > found.log

FOR /F %%A IN (found.log) DO IF %%~zA EQU 0 GOTO end

start notepad.exe

:end

del search.log
del found.log

I hope this helps someone else.

Upvotes: 6

vtrz
vtrz

Reputation: 641

Under Windows you can use Windows Management Instrumentation (WMI) to ensure that no apps with the specified command line is launched, for example:

wmic process where (name="nmake.exe") get commandline | findstr /i /c:"/f load.mak" /c:"/f build.mak" > NUL && (echo THE BUILD HAS BEEN STARTED ALREADY! > %ALREADY_STARTED% & exit /b 1)

Upvotes: 21

Adam
Adam

Reputation:

I use PV.exe from http://www.teamcti.com/pview/prcview.htm installed in Program Files\PV with a batch file like this:

@echo off
PATH=%PATH%;%PROGRAMFILES%\PV;%PROGRAMFILES%\YourProgram
PV.EXE YourProgram.exe >nul
if ERRORLEVEL 1 goto Process_NotFound
:Process_Found
echo YourProgram is running
goto END
:Process_NotFound
echo YourProgram is not running
YourProgram.exe
goto END
:END

Upvotes: 8

Motti
Motti

Reputation: 114805

I don't know how to do so with built in CMD but if you have grep you can try the following:

tasklist /FI "IMAGENAME eq myApp.exe" | grep myApp.exe
if ERRORLEVEL 1 echo "myApp is not running"

Upvotes: 3

ChrisLively
ChrisLively

Reputation: 88082

I'm assuming windows here. So, you'll need to use WMI to get that information. Check out The Scripting Guy's archives for a lot of examples on how to use WMI from a script.

Upvotes: 0

Related Questions