Reputation: 65586
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
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.
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
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
Upvotes: 0
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
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.
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).
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.
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
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
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 omittedSee the man page of the tasklist command for the whole syntax.
Upvotes: 393
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:
:: 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
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
Reputation: 895
TASKLIST | FINDSTR ProgramName || START "" "Path\ProgramName.exe"
Upvotes: 32
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
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
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
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
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
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:
c:\windows\notepad.exe
)The same can be checked by getting the parent process name.
Upvotes: -3
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
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
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
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
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
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
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