I'm trying to write code which loops and tells if a certain process is opened or not. It should be a loop that will show me in real time if the process is opened or not. In other words, a text will change when the program is opened and change again when it's closed. Instead what I got was a flood with the same text and it doesn't show the 'echos' below it.
I tried this:
#echo off
goto xera
:start
tasklist /FI "IMAGENAME eq notepad.exe" | find /I "notepad.exe" > nul
IF %ERRORLEVEL% equ 0 ECHO is opened
IF %ERRORLEVEL% equ 1 ECHO isnt opened
:xera
set /p "=Status: " <nul &call :start //the code got 'stuck' here
ECHO Text 2 (doesnt show)
pause>nul
You are not terminating your sub-routine :start correctly. Try the following:
#echo off
goto xera
:start
tasklist /FI "IMAGENAME eq notepad.exe" | find /I "notepad.exe" > nul
IF %ERRORLEVEL% equ 0 ECHO is opened
IF %ERRORLEVEL% equ 1 ECHO isnt opened
exit /b
:xera
set /p "=Status: " <nul &call :start
ECHO Text 2 (doesnt show)
pause>nul
I inserted exit /b which tells the command interpreter to return to the command after the call statement that actually called it. You could also use goto :EOF instead. Type call /? for more information on how to call sub-routines in batch.
Related
I wanted to call waiting spinner on my batch script like this is my code:
#echo off
::-----------------------------Waiting-Spinner-------------------------------
:spinner
set mSpinner=%mSpinner%.
if %mSpinner%'==....' (set mSpinner=.)
cls
::----------------------------Subdomain-Script-------------------------------
echo Enumerating Subdomains From Script1 %mSpinner%
python2 enumsubdomain.py google.com > google.txt
SLEEP 1
goto spinner
echo Enumerating Subdomains From Script2 %mSpinner%
python2 enumsubdomain2.py yahoo.com > yahoo.txt
SLEEP 1
goto spinner
#pause
And this spinner text output should be something like this:
Enumerating Subdomains From Script1 ...<Here this dot will be animated]
Enumerating Subdomains From Script2 ...<Here this dot will be animated]
But it only outputs first line(Script1) and the 2nd script stops and doesn't outputs the 2nd line as well, i guess it's because of goto line in batch script and i have no idea what can be done here to make it work!
After clarification in your comment, you'd probably want something like this.
We start the commands in separate windows with new window titles per window, then we use tasklist to determine of either are still running. So both results will be echo if both run, or only one will be echod if only one runs.
#echo off
set done1=1
set done2=1
start "enum1" /min cmd.exe /C ^(python2 enumsubdomain.py google.com ^> google.txt^)
start "enum2" /min cmd.exe /C ^(python2 enumsubdomain2.py yahoo.com ^> yahoo.txt^)
:spinner
set mSpinner=%mSpinner%.
if "%mSpinner%"=="...." (set mSpinner=.)
cls
(tasklist /FI "WINDOWTITLE eq enum1" | findstr /i "cmd")>nul && echo Enumerating Subdomains From Script1 %mSpinner% || set done1=0
(tasklist /FI "WINDOWTITLE eq enum2" | findstr /i "cmd")>nul && echo Enumerating Subdomains From Script2 %mSpinner% || set done2=0
if %done1% equ 0 if %done2% equ 0 goto :eof
(sleep 1)>nul
goto :spinner
You want to use the spinner several times, so using a subroutine for the spinner is a good idea.
The main problem is: batch waits for a command to end before continuing with the next one, so you can't include the python command into the loop. Solution: start the command as an independent process and then start the "spinning loop". Break it, when the independent process does not exist anymore.
Here the subprocess :spinner takes a string as parameter (the message), making it flexible.
Instead of clearing the screen (cls) for each iteration of the loop, I took a different approach: overwriting the line over and over (less flickering and keeping the previous screen intact)
#echo off
setlocal EnableDelayedExpansion
REM create a CariageReturn:
for /f %%a in ('copy /Z "%~dpf0" nul') do set "CR=%%a"
cls
start /min "MySpinnerProcess" cmd /c "timeout 8 >google.txt"
call :spinner "Enumerating Subdomains From Script1"
start /min "MySpinnerProcess" cmd /c "timeout 5 >yahoo.txt"
call :spinner "Enumerating Subdomains From Script2"
echo finished.
goto :eof
:spinner
tasklist /fi "WindowTitle eq MySpinnerProcess" 2>nul | findstr /bilc:"cmd.exe" >nul || (
echo %~1 done.
goto :eof
)
set "mspinner=%mspinner%."
if %mspinner% == .... set "mspinner=."
<nul set /p ".=%~1 %mspinner% !CR!"
timeout 1 >nul
goto :spinner
For completeness my script for running the two scripts simultaneously. (very similar to Gerhard's solution, but keeping two spinners) (in my defense: I already had it ready when I asked you if you want them one after the other or simultaneously, but I just had to spend the sunny Sunday Afternoon outside)
#echo off
setlocal
::---------Waiting-Spinner---------
start /min "MyPhytonProcess1" cmd /c "timeout 8 >google.txt"
start /min "MyPhytonProcess2" cmd /c "timeout 5 >yahoo.txt"
:spinner
set mSpinner1=%mSpinner1%.
set mSpinner2=%mSpinner2%.
if %mSpinner1%==.... (set mSpinner1=.)
if %mSpinner2%==.... (set mSpinner2=.)
cls
::----------------------------Subdomain-Script-------------------------------
echo Enumerating Subdomains From Script1 %mSpinner1%
echo Enumerating Subdomains From Script2 %mSpinner2%
if "%mSpinner1%%mSpinner2%" == "Done.Done." goto :done
tasklist /FI "WindowTitle eq MyPhytonProcess1" 2>nul |find "cmd.exe" >nul || set "mSpinner1=Done"
tasklist /FI "WindowTitle eq MyPhytonProcess2" 2>nul |find "cmd.exe" >nul || set "mSpinner2=Done"
timeout 1 >nul
goto :spinner
:done
echo finished.
#pause
I have a program which has following flow. Problem is the windows batch file doesn't properly checks errorlevel and doesn't set KILLSTS value. Could you please let me know what's wrong with this program and how to fix this?
Ask user to open an exe
if Yes
check exe is running or not
if running, ask user whether to close that exe
if yes close exe
run the exe
else
exit
Here is the sample batch file.
#ECHO OFF
#REM SETLOCAL ENABLEEXTENSIONS
SET /P AREYOUSURE="Open Spring STS [y/n]>"
set AREYOUSURE=%AREYOUSURE:~0,1%
ECHO AREYOUSURE=%AREYOUSURE:~0,1%
IF /I %AREYOUSURE% == N (
SET /A errno^|=%ERROR_OTHERCOMMAND_FAILED%
echo Existing Batch
EXIT /B %errno%
)
SETLOCAL
#REM SET KILLSTS=Y
tasklist /fi "IMAGENAME eq STS.exe" |find ":" > nul
ECHO Error %errorlevel%
IF %errorlevel% neq 0 (
SETLOCAL
SET /P KILLSTS="Spring STS is running. Kill STS Process [y/n]>"
echo KILLSTS %KILLSTS%
set KILLSTS=%KILLSTS:~0,1%
echo KILLSTS AFTER SUBSTR %KILLSTS%
IF /I %KILLSTS% == Y TASKKILL /f /im "STS.exe"
ENDLOCAL
)
START "" "C:\sts-bundle\sts-3.8.3.RELEASE\STS.exe"
I am getting below error
You need to learn how to properly format if statements.
You are formatting them as:
IF /I %KILLSTS% == Y TASKKILL /f /im "STS.exe"
When they should be formatted as:
if /i "%KILLSTS%"=="Y" (TASKKILL /f /im STS.exe)
The formatting doesn't really matter as such in simple batch files, but it's best to use the correct syntax which can handle special characters such as SPACES, AMPERSANDS, QUOTES, PIPE for when more complex variables are involved.
Updated script:
#ECHO OFF
#REM SETLOCAL ENABLEEXTENSIONS
SET /P "AREYOUSURE=Open Spring STS [y/n]>"
set "AREYOUSURE=%AREYOUSURE:~0,1% "
echo "AREYOUSURE=%AREYOUSURE:~0,1%"
IF /I "%AREYOUSURE%"=="N" (
SET /A errno^|=%ERROR_OTHERCOMMAND_FAILED%
echo Existing Batch
EXIT /B %errno%
)
SETLOCAL
#REM SET KILLSTS=Y
tasklist /fi "IMAGENAME eq STS.exe" | find ":" > nul
ECHO Error %errorlevel%
IF "%errorlevel%" neq "0" (
call :escapeexpansion
)
START "" "C:\sts-bundle\sts-3.8.3.RELEASE\STS.exe"
exit /b
:escapeexpansion
SETLOCAL
SET /P "KILLSTS=Spring STS is running. Kill STS Process [y/n]>"
echo KILLSTS %KILLSTS%
set "KILLSTS=%KILLSTS:~0,1%"
echo KILLSTS AFTER SUBSTR %KILLSTS%
IF /I "%KILLSTS%"=="Y" TASKKILL /f /im "STS.exe"
ENDLOCAL
goto :EOF
The entire structure seems wrong to me; as well as pointlessly using SET /P instead of CHOICE.
#ECHO OFF
TASKLIST /FI "IMAGENAME eq STS.exe"|FIND ":">NUL 2>&1&&GOTO ASKIF
CHOICE /M "Spring STS is running. Kill STS Process"
IF ERRORLEVEL 2 GOTO ENDIT
TASKKILL /F /IM "STS.exe"
TIMEOUT 3 /NOBREAK>NUL
:ASKIF
CHOICE /M "Open Spring STS"
IF ERRORLEVEL 2 GOTO ENDIT
START "" "C:\sts-bundle\sts-3.8.3.RELEASE\STS.exe"
:ENDIT
Echo=Exiting Batch
TIMEOUT 3 /NOBREAK>NUL
I want to create a batch file, that detects when the CMD window gets closed and does directly after an action.
If for example the the textfile text.txt is opened, and after i launch the batch file called prog.bat with this code inside:
#echo off
echo Hello
pause
How can i tell the batch that if the CMD window which is currently opend, should taskill text.txt file when somebody closes the CMD window?(by closing with ending process or hitting the X on the top)
This is the accepted answer at this question:
#echo off
if "%1" equ "Restarted" goto %1
start "" /WAIT /B "%~F0" Restarted
echo Execute here anything you want when the Batch file is closed...
goto :EOF
:Restarted
echo Hello
pause
exit
Im not one of the best but you could do this.
You need to create 3 files.
Start.bat
#echo off
TITLE Start.bat
REM :: THIS FILE OPEN'S THE CHECK WINDOW BATCH ::
start Init2.bat
ping localhost -n 1 >nul
REM :: IF I WOULDN'T HAVE THE "PING" THE CHECK WINDOW BATCH WOULD BE ON TOP ::
start Init1.bat
Init1.bat
#echo off
REM :: YOU CAN WRITE WHAT YOU WANT HERE ::
REM :: YOUST REMEMBER TO CHANGE "Init2.bat" WHEN YOU CHANGE THE TITLE ::
TITLE Init1.bat
echo.HELLO
pause>nul
Init2.bat
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
TITLE Init2.bat
:Start
REM :: GET PID ::
set "PID="
for /F "skip=3 delims=" %%A in ('TASKLIST /FI "WINDOWTITLE eq Init1.bat"') do (
set "A=%%A"
set "A=!A:~26,8!"
set "A=!A: =!"
set "PID=!A!"
set "A="
echo.!PID!
goto Test
)
REM :: IF NO WINDOWS NAMED "Init1.bat" exit ::
if not defined "PID" (
echo.No Window!
goto Exit
)
:Test
set "true=0"
for /F "skip=3 delims=" %%A in ('TASKLIST /FI "PID eq !PID!"') do (
set "true=1"
)
REM :: IF WINDOW CLOSED ::
if "!true!" EQU "0" (
echo.No Window!
goto Exit
)
goto Test
:Exit
REM :: HERE ARE YOU WRITING THE CLOSING FILE ::
ren testtxt.txt prog.bat
echo.#echo off > prog.bat
echo.echo hello >> prog.bat
echo.pause >> prog.bat
exit
Hope this help.
This is not a wiki answer, but i hope you find it helpful.
The only thing is that you have a window behind the first, but it works.
The past few days I have been working on a script that I thought would be rather easy but it seems not, and I do understand why. My problem is how to get around it.
The batch script I need explained:
I have a script that runs in cmd.exe that does a bunch of things like moving a huge amount of files from a location to another. Lets call it
movefile.cmd. This script works, but happens to stop sometimes (very rarely - lets not go into why and that script). Its important that this script always runs, so my idea here was to create a batch that exits cmd.exe and then re-opens the script each hour or so. Lets call this script restartcmd.bat
Sounds perfectly easy as I could do this:
#echo off
:loop
start c:\script\movefile.cmd
Timeout /nobreak /t 3600
Taskkill cmd.exe
goto loop
But obviously this doesn't work because my new script also runs in cmd.exe, so it would kill this process as well.
What I've tried:
So I made a copy of cmd.exe and renamed it into dontkillthis.exe. I run dontkillthis.exe and then open the restardcmd.bat from dontkillthis.exe - this works perfectly! But I need to be able to just dobbleclick my script instead of doing that. Why? Because its supposed to be as easy as possible and I want my restartcmd.bat to be in my startup folder.
I've been looking at the ideas of getting the exact process ID of cmd.exe and shutting that so that my dontkillthis.exe will remain, but I can't seem to nail it. Tried all thats written in here how to kill all batch files except the one currently running , but I can't get it to work.
I'm not sure if I'm being confused or if it actually is a bit hard to do this.
I'd really appreciate some help here.
Best Regards
MO
first you'll need the PID of the current CMD instance. The topic has been discussed here . I will offer you my solution - getCmdPID.bat
and here's the script (getCmdPID should in the same directory ):
#echo off
call getCmdPID
set "current_pid=%errorlevel%"
for /f "skip=3 tokens=2 delims= " %%a in ('tasklist /fi "imagename eq cmd.exe"') do (
if "%%a" neq "%current_pid%" (
TASKKILL /PID %%a /f >nul 2>nul
)
)
Normally with the following command I should be able to find the PID. Unfortunately this is not the case.
title exclude &tasklist /NH /v /fo csv /FI "WINDOWTITLE ne exclude*" /FI "IMAGENAME eq cmd.exe" /FI "STATUS eq running"
So to achieve my goal, I used the following command:
FIND /I "exclude" 1>NUL
#echo off
TITLE exclude
(for /f "usebackq tokens=*" %%a in (`tasklist /NH /v /fo csv /FI "IMAGENAME eq cmd.exe" /FI "STATUS eq running"`) do (
(
echo %%a | FIND /I "exclude" 1>NUL
) || (
for /f "usebackq tokens=2 delims=," %%i in (`echo %%a`) do (
echo TASKKILL /PID %%~i /f
)
)
)
)>_output-taskill.txt
TYPE _output-taskill.txt
Another approach to kill all the processes in a single line is to use filters on the command taskkill with filters should look like:
TASKKILL /F /FI "PID ne XXXX" /FI "IMAGENAME eq cmd.exe" /IM cmd.exe
eq (equal)
ne (not equal)
gt (greater than)
lt (lesser than)
#echo off
TITLE exclude
(for /f "usebackq tokens=2 delims=," %%a in (`tasklist /NH /v /fo csv /FI "IMAGENAME eq cmd.exe" /FI "STATUS eq running" ^| FIND /I "exclude"`) do (
echo TASKKILL /F /FI "PID ne %%~a" /FI "IMAGENAME eq cmd.exe" /IM cmd.exe
)
)>_output-taskill.txt
TYPE _output-taskill.txt
I have found a solution that utilizes text files to keep track of all previous PIDs the bat file has had. It attempts to kill them silently and then adds the current PID to the list after.
If you don't want it to kill the old, already existing process, simply replace the line that has "taskkill" with whatever you were wanting to do with it.
(might require you to run as admin in order to have permissions to kill the duplicate process. see permission elevation code below for optional implementation if you don't want to have to run as admin every time.)
#echo off
set WorkingDir=%cd%
if exist MostRecentPID.txt ( del "PIDinfo.txt" /f /q ) > nul
cd ..\..\..\..\..\..\..
title mycmd
tasklist /v /fo csv | findstr /i "mycmd" > %WorkingDir%\PIDinfo.txt
set /p PIDinfo=<%WorkingDir%\PIDinfo.txt
REM below, the 11 means get substring starting a position 11 with length of 5 characters. The tasklist command gives a long and verbose value so this will get just the PID part of the string.
set PID5chars=%PIDinfo:~11,5%
set PID4chars=%PIDinfo:~11,4%
if exist PreviousPIDs.txt (
for /F "tokens=*" %%A in (PreviousPIDs.txt) do taskkill.exe /F /T /PID %%A > nul 2>&1
goto CheckIfFourCharPID
)
:CheckIfFourCharPID
if %PID4chars% gtr 8100 (
for /F "tokens=*" %%A in (PreviousPIDs.txt) do taskkill.exe /F /T /PID %%A > nul 2>&1
echo %PID4chars% >> "PreviousPIDs.txt"
) else (
echo %PID5chars% >> "PreviousPIDs.txt"
)
Explanation: (warning: very technical)
-This solution gets a substring of the tasklist command to get just the PID. There will not be a PID for cmd.exe that is greater than 18100 so check if PID4chars is greater than 8100 so we know if it's a 4 digit or 5 digit number
case 1: a 5 digit PID like 17504 has a PID5chars val 17504 and a PID4chars val of 1750, so we add PID5chars to the text files of PIDs to kill
case 2: a 4 digit PID like 8205 has a PID5chars val of 8205" and a PID4chars val of 8205, so we add PID4chars to the text files of PIDs to kill
case 3: a 4 digit PID like 4352 has a PID5chars val of 4352" and a PID4chars val of 4352, so we add PID4chars to the text files of PIDs to kill
OPTIONAL PERMISSION ELEVATION CODE
(put this at the top of your bat file and it will auto-run it as admin.)
#echo off
setlocal DisableDelayedExpansion
set "batchPath=%~0"
for %%k in (%0) do set batchName=%%~nk
cd ..\..\..\..\..\..\..\..
if exist %cd%\Temp (
set temp=%cd%\Temp
goto vbsGetPrivileges
)
if exist %cd%\Windows\Temp (
set temp=%cd%\Windows\Temp
goto vbsGetPrivileges
)
set temp=%cd%
:vbsGetPrivileges
set "vbsGetPrivileges=%temp%\OEgetPriv_%batchName%.vbs"
setlocal EnableDelayedExpansion
:CheckIfRunningAsAdmin
net session >nul 2>&1
if %ERRORLEVEL% == 0 (
goto gotPrivileges
) else ( goto ElevatePermissions )
:ElevatePermissions
if '%1'=='ELEV' (echo ELEV & shift /1 & goto gotPrivileges)
ECHO Set UAC = CreateObject^("Shell.Application"^) > "%vbsGetPrivileges%"
ECHO args = "ELEV " >> "%vbsGetPrivileges%"
ECHO For Each strArg in WScript.Arguments >> "%vbsGetPrivileges%"
ECHO args = args ^& strArg ^& " " >> "%vbsGetPrivileges%"
ECHO Next >> "%vbsGetPrivileges%"
ECHO UAC.ShellExecute "!batchPath!", args, "", "runas", 1 >> "%vbsGetPrivileges%"
"%SystemRoot%\System32\WScript.exe" "%vbsGetPrivileges%" %*
exit /B
:gotPrivileges
setlocal & pushd .
cd /d %~dp0
if '%1'=='ELEV' (del "%vbsGetPrivileges%" 1>nul 2>nul & shift /1)
net session >nul 2>&1
if %ERRORLEVEL% == 0 (
goto Continue
) else (
REM unable to elevate permissions so tell user to run file as admin manually
echo Please re-run this file as administrator. Press any key to exit...
pause > nul
goto Exit
)
:Continue
<insert rest of code here>
It would be much better to get the PID of your movefile.cmd. If you can edit it, add a title MyMoveFileProcess and get it's PID with
for /f "tokens=2" %%i in ('tasklist /v ^|find "MyMoveFileProcess"') do set PID=%%i
Then you can kill it with taskkill /pid %pid%
Instead of changing your movefile.cmd, you can also just start it with an title:
start "MyMoveFileProcess" c:\script\movefile.cmd
A couple of lines will help you achieve this:
TITLE exclude
taskkill /IM cmd.exe /FI "WINDOWTITLE ne exclude*"
I'm trying to write a batch that checks how many instances of the process "example.exe" are running, and if there are two or more instances, leave it running. But if there is only one instance running, end the process. Here's what I have:
#echo off
wmic process where name="example.exe" | find "example" /c > %temp%\variable.txt
set /p value=<%temp%\variable.txt
if %value% lss 2 goto endprocess
if %value% gtr 1 goto continue
:endprocess
start taskkill /f /im example.exe
:continue
ECHO continue
#echo off
My issue is this: It always thinks value is lss 2 (it thinks there are less than 2 instances of the process running). However, in my task manager, I can see that there is obviously 2 instances running. I think it's an issue with defining the value maybe? I don't know, I'm quite new to this. Any help? Thanks!
UPDATE
Okay I've now changed it to this (suggested by Magoo)
#echo off
wmic process where name="example.exe" | find "example" /c > "%temp%\variable.txt"
set /p value=<"%temp%\variable.txt"
if %value% equ 1 goto endprocess
if %value% neq 1 goto continue
:endprocess
start taskkill /f /im example.exe
:continue
ECHO continue
#echo off
This still doesn't exactly work, but i changed the number of instances from 1 to 0 and it ended the process. In other words, 1 process was running, but this batch file thought that 0 were running. Any ideas now?
This uses tasklist in XP Pro and higher:
#echo off
tasklist /fi "imagename eq example.exe" /nh |find /i /c "example.exe" > "%temp%\variable.txt"
set /p value=<"%temp%\variable.txt"
if %value% equ 1 taskkill /f /im example.exe
ECHO continue
#echo off
You can do it with one line and no temp file also - this uses another findstr filter to check if the number is a single 1 on a line and then && is a conditional operator that will launch taskkill if it does find 1.
#echo off
tasklist /fi "imagename eq example.exe" /nh |find /i /c "example.exe"|findstr "^1$" >nul && taskkill /f /im example.exe
ECHO continue
#echo off
I'd suggest that you have a fault with your logic.
The code should go to endprocess if the number found is <2 - that is, 0 or 1. If the lss 2 test is failed, then the count must be 3+, so the gtr 1 test will always succeed.
I've no idea why you don't use simply
if %value% neq 1 goto continue
or even
if %value% equ 1 start taskkill /f /im example.exe
But probably you've not told us that you want to be able to detect other instance-counts - as well as concealing the name of the executable for which you are checking.
Now - it may have been really useful to show us the content of the file. Are you sure the file is actually being generated? What happens if you try using "%temp%\variable.txt" instead of %temp%\variable.txt - that is, "quote the filename" ?