I wrote this small script to kill Spotify whenever the title of the window is "Advertisement". For now, it just looks for the spotify.exe process and, if the name of the window matches, kills it (next step is executing it every second). However, I get an error every time I execute it, telling me that there's an unexpected ( in IF /i "%A:~0,4" (, but such statement is not in my code: it seems like Windows modifies IF /i "%%A:~0,4%"=="PID:" ( before executing it.
Here's the script:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
tasklist /fi "imagename eq spotify.exe" /fo list /v > tmp.txt
FOR /F "usebackq tokens=*" %%A IN ("tmp.txt") DO (
SET test=%%A
echo %%A
IF /i "%%A:~0,4%"=="PID:" (
SET "pid=%%A:PID: =%"
echo %pid%
)
IF /i "%%A:~0,13%"=="Window Title:" (
SET "wintitle=%%A:Window Title: =%"
echo %wintitle%
)
IF "%wintitle%"=="Advertisement" (
taskkill /F /PID %pid%
)
)
PAUSE
Error message (with echo on):
C:\Users\marco\Desktop>antispotify.bat
C:\Users\marco\Desktop>tasklist /fi "imagename eq spotify.exe" /fo list /v 1>tmp.txt
( was unexpected at this time.
C:\Users\marco\Desktop> IF /i "%A:~0,4" (
Does anyone know what's wrong with my code?
The task to force a real kill of Spotify process running for advertisement can be done also by using following batch file without using delayed expansion.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "tokens=1* delims=:" %%I in ('%SystemRoot%\System32\tasklist.exe /FI "imagename eq spotify.exe" /FO LIST /V 2^>nul') do (
if /I "%%~I" == "PID" (
for /F %%K in ("%%~J") do set "ProcessIdentifier=%%~K"
) else if /I "%%~I" == "Window Title" (
for /F "tokens=*" %%K in ("%%~J") do if /I "%%~K" == "Advertisement" call %SystemRoot%\System32\taskkill.exe /F /PID %%ProcessIdentifier%%
)
)
endlocal
The same code with using delayed environment variable expansion:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /F "tokens=1* delims=:" %%I in ('%SystemRoot%\System32\tasklist.exe /FI "imagename eq spotify.exe" /FO LIST /V 2^>nul') do (
if /I "%%~I" == "PID" (
for /F %%K in ("%%~J") do set "ProcessIdentifier=%%~K"
) else if /I "%%~I" == "Window Title" (
for /F "tokens=*" %%K in ("%%~J") do if /I "%%~K" == "Advertisement" %SystemRoot%\System32\taskkill.exe /F /PID !ProcessIdentifier!
)
)
endlocal
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.
call /?
echo /?
endlocal /?
for /?
if /?
setlocal /?
taskkill /?
tasklist /?
See also: How does the Windows Command Interpreter (CMD.EXE) parse scripts?
As noted in the comments, substring manipulation doesn't work on for variables (%%a type). Instead, you need an ordinary variable and of course delayed expansion.
But may I suggest another approach:
#ECHO OFF
SETLOCAL
for /f "tokens=2,9 delims=," %%a in ('tasklist /fi "imagename eq notepad.exe" /fo csv /v /nh') do (
set "pid=%%~a"
set "wintitle=%%~b"
)
set pid
set wintitle
IF "%wintitle%"=="Advertisement" taskkill /F /PID %pid%
Here we use the command directly with the for loop instead of using a temporary file. Besides that, we change the output format to csv (easier to parse) with no header line ( /nh)
( I used notepad.exe, because I don't have spotify, but that's easy to adapt)
Related
I'm working on a cmd script and want to find some processes in the tasklist. So what I have is an array like "prog[0]=devcpp.exe, prog[1]=notepad.exe..."
And to find these processes I'm using FOR command. Ok, but when I execute the command "tasklist /fi" it seems won't recognize the array, and don't give me the expected result.
The code is:
set prog[0]=devcpp.exe
set prog[1]=notepad.exe
set prog[2]=calc.exe
FOR /l %%a IN (0,1,2) DO (
tasklist /fi "IMAGENAME eq %prog[%%a]%"
)
But the result is:
error: the search filter cannot be recognized
And of course I am running these processes...
So, any suggestions?
First, remove the trailing QUOTATION MARK character on the prog[2] setting.
Secondly, when invoking tasklist, the PERCENT character must be doubled to retain one. And, use the command line interpreter. %ComSpec%, to interpret the variable.
set prog[0]=devcpp.exe
set prog[1]=notepad.exe
set prog[2]=powershell.exe
FOR /l %%a IN (0,1,2) DO (
"%ComSpec%" /C tasklist /fi "IMAGENAME eq %%prog[%%a]%%"
)
You have given no clue as to what the end goal is about. It would likely be easier to code this in PowerShell as #Bill_Stewart suggested.
Here is my suggestion for this task with a commented batch file.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Delete all environment variables of which name starts with prog[.
for /F "delims==" %%I in ('set prog[ 2^>nul') do set "%%I="
rem Define environment variables with the executables to get listed.
set "prog[0]=devcpp.exe"
set "prog[1]=notepad.exe"
set "prog[2]=calc.exe"
rem Run TASKLIST in a loop to get output a list of those processes
rem from the list defined above which are currently running with the
rem header output on English Windows just on first running process.
set "Header=1"
for /F "tokens=1* delims==" %%I in ('set prog[') do if defined Header (
%SystemRoot%\System32\tasklist.exe /FI "IMAGENAME eq %%J" 2>&1 | %SystemRoot%\System32\findstr.exe /B /I /L /V /C:"INFO:"
if not errorlevel 1 set "Header="
) else (
%SystemRoot%\System32\tasklist.exe /NH /FI "IMAGENAME eq %%J" 2>nul
)
endlocal
To understand the commands used and how they work, open a command prompt window, execute there the following commands, and read the displayed help pages for each command, entirely and carefully.
echo /?
endlocal /?
findstr /?
for /?
if /?
rem /?
set /?
setlocal /?
tasklist /?
How to kill specific VBScript using batch file?
#echo off
set vbs="%temp%\dummy.vbs"
for /f "usebackq tokens=2" %%s in (`WMIC path Win32_Process where 'name="wscript.exe"' get commandline,processid | findstr /i /c:"%vbs%"`) do (
taskkill /f /fi "pid eq %%s"
)
I also tried the code below but it seems that commandline like is not working.
WMIC path Win32_Process where "name='wscript.exe' and commandline like %vbs%" get processid
Thanks in advance!
Incorporated items in the comments and escaped special characters with ^.
#echo off
set "vbs=%temp%\dummy.vbs"
for /f "usebackq tokens=3" %%s in (
`WMIC process where "name='wscript.exe'" get commandline^,processid ^| findstr /i /c:"%%vbs%%"`
) do (
taskkill /f /fi "pid eq %%s"
)
I am trying to write a batch script that saves the result of a command in a variable. so I can use it later.
For example I am tryin to run this on the script:
sc queryex "Service" |find /i "pid"
but I want to save this result in a variable.
set PIDRS=sc queryex "Themes" |find /i "pid"
ECHO "%PIDRS%
Any Ideas?
for /f "tokens=* delims=" %%# in ('sc queryex "Themes" ^|find /i "pid"') do set "PIDRS=%%#"
echo %PIDRS%
This will set the entire line to PIDRS
here's how to get only the pid:
#echo off
set "rspid="
for /f "skip=9 tokens=2 delims=:" %%# in ('sc queryex "Themes"') do (
if not defined rspid set /a rspid=%%#
)
the second does not use additional FIND which in theory should make it faster.
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*"
In Windows batch scripting there is start command which starts a new process.
Is it possible to get PID of the process just started?
This is an old post but I think that it worth to share the following 'easy to use' solution which works fine nowadays on Windows.
Start multiple processes in parallel:
start "<window title>" <command will be executed>
Example:
start "service1" mvn clean spring-boot:run
start "service2" mvn clean spring-boot:run
Obtain the PID of the processes (optional):
tasklist /V /FI "WindowTitle eq service1*"
tasklist /V /FI "WindowTitle eq service2*"
Kill the processes:
taskkill /FI "WindowTitle eq service1*" /T /F
taskkill /FI "WindowTitle eq service2*" /T /F
You can in batch but not directly per say. You need to either parse the output of tasklist.exe or use wmic.exe. Both require you to know what you just started which of course you will.
Using tasklist.exe:
for /F "TOKENS=1,2,*" %a in ('tasklist /FI "IMAGENAME eq powershell.exe"') do set MyPID=%b
echo %MyPID%
To use this in a batch script double up the percent signs.
Using wmic.exe:
for /f "TOKENS=1" %a in ('wmic PROCESS where "Name='powershell.exe'" get ProcessID ^| findstr [0-9]') do set MyPID=%a
echo %MyPID%
If there are processes already running with the same name, you first need to get a list of the current pids, than start your local process(es) and then check the pids again. Here is a sample code that starts 3 process and kills them at the end (specifically the ones started locally):
#echo off
set PROCESSNAME=notepad.exe
::First save current pids with the wanted process name
setlocal EnableExtensions EnableDelayedExpansion
set "RETPIDS="
set "OLDPIDS=p"
for /f "TOKENS=1" %%a in ('wmic PROCESS where "Name='%PROCESSNAME%'" get ProcessID ^| findstr [0-9]') do (set "OLDPIDS=!OLDPIDS!%%ap")
::Spawn new process(es)
start %PROCESSNAME%
start %PROCESSNAME%
start %PROCESSNAME%
::Check and find processes missing in the old pid list
for /f "TOKENS=1" %%a in ('wmic PROCESS where "Name='%PROCESSNAME%'" get ProcessID ^| findstr [0-9]') do (
if "!OLDPIDS:p%%ap=zz!"=="%OLDPIDS%" (set "RETPIDS=/PID %%a !RETPIDS!")
)
::Kill the new threads (but no other)
taskkill %RETPIDS% /T > NUL 2>&1
endlocal
you can try with
wmic process call create "notepad"
which will return the pid of the created process.
Processing this with FOR
setlocal
set "ReturnValue="
set "ProcessId="
for /f "eol=} skip=5 tokens=1,2 delims=;= " %%a in ('wmic process call create "notepad"') do (
set "%%a=%%b"
)
echo %ReturnValue%
echo %ProcessId%
endlocal
PowerShell can be used for this:
powershell -executionPolicy bypass -command "& {$process = start-process $args[0] -passthru -argumentlist $args[1..($args.length-1)]; exit $process.id}" notepad test.txt
echo Process ID of new process: %errorlevel%
This is the code that i use to get a PID
for /f "tokens=2 delims=," %%a in ('tasklist /FO CSV ^| findstr /I /C:"entertheprocess.here"') do (
echo PID:%%a
)
#ECHO OFF
SETLOCAL EnableDelayedExpansion EnableExtensions
::
::weil es mehrere Sessions für UltraCompare gleichzeitig geben kann, es wird hier die
::neueste Instanz (soeben gestartet) ermittelt, um später mit ProcessId diese Instanz
::aktivieren zu können...
::
set "CreationDate="
set /A "ProcessIdUltraCompare=0"
::
set /A "lineno=0"
FOR /F %%T IN ('Wmic process where^(Name^="uc.exe"^) get CreationDate^|sort /r') DO (
set /A lineno+=1
rem echo %%T
rem echo !lineno!
if !lineno! equ 2 (
rem ECHO %%T
set CreationDate=%%T
rem echo !CreationDate!
set /A "lineno=0"
FOR /F %%P IN ('Wmic process where^(CreationDate^="!CreationDate!"^) get ProcessId') DO (
set /A lineno+=1
rem echo %%P
if !lineno! equ 2 (
set "ProcessIdUltraCompare=%%P"
rem echo ProcessIdUltraCompare=!ProcessIdUltraCompare!
goto :l_pid_uc_got
)
)
)
)
:l_pid_uc_got
echo ProcessIdUltraCompare=!ProcessIdUltraCompare!
PAUSE