bat file loop stops too early - windows

I am using this piece of code to run a loop within a list of active processes to identify a process by name to change the priority.
TIMEOUT /T 1
for /F "tokens=1,2" %%i in ('tasklist /FI "IMAGENAME eq java.exe" /fo table /nh') do set pid=%%j
echo %pid%
wmic process where processid=%pid% CALL setpriority 128
exit
My problem is I have more than one process called "java.exe" but I want them all to be effected by my code. How can I achieve this?

for /F "tokens=1,2" %%i in ('tasklist /FI "IMAGENAME eq java.exe" /fo table /nh') do (
echo %%j
wmic process where processid=%%j CALL setpriority 128
)
(untested)

This method will allow you to use the variable pid, but you will need to use delayed expansion.
SETLOCAL ENABLEDELAYEDEXPANSION
for /F "tokens=1,2" %%i in ('tasklist /FI "IMAGENAME eq java.exe" /fo table /nh') do set pid=%%j
echo !pid!
wmic process where processid=!pid! CALL setpriority 128
exit
The setlocal line is to allow cmd to process the variables at run-time, not phrase-time.
! denotes to process the variable at run-time, not phrase-time.

Related

Remove comma from variable in CMD

How are you all doing?
I made a batch file that gets the memory usage of a process that has a specific PID...
Here is the code:
#Echo off
REM Get memory from process using tasklist
for /f "tokens=2 delims=," %A in ('tasklist /svc /fi "SERVICES eq .Service02" /FO csv /NH') do (
REM Store PID on a variable
#Set "pidSlv02=%~A"
REM Get memory usage from process with that PID
for /f "tokens=5 delims= " %B in ('tasklist /fi "pid eq %pidSlv02%" /NH') do (
REM Store mem value on another variable
#Set "memSlv02=%~B"
REM Remove commas from variable
#Echo %memSlv02% K
)
)
So, the output is:
1,407,356
Now, i need to convert that number to be 1407356
I did not come here directly. I already tried a couple of things but without success.
Im still very new and learning CMD/batch scripts... Didnt even know t was possible to do so much on Windows. Thought it was only possible on Linux. So please don't be angry with me if i dont understand something. Im trying my best! 😣
Also, sorry if i mispelled something here since this is not my native language.
I tried adding the code:
set memSlv02=!memSlv02:,=!
set /a total=!total! + !memSlv02!
But if i use echo %total% i receive Missing operator. 0
Any tips?
Here's is an example which should output the data you require using the commands you've decided are best for you!
#Echo Off & SetLocal EnableExtensions DisableDelayedExpansion
Set "SvcName=.Service02"
For /F Tokens^=3^ Delims^=^" %%G In ('%SystemRoot%\System32\tasklist.exe
/Fi "Services Eq %SvcName%" /Fo CSV /NH /Svc 2^>NUL') Do Set "PID=%%G"^
& For /F Tokens^=9^ Delims^=^" %%H In ('%SystemRoot%\System32\tasklist.exe
/Fi "PID Eq %%G" /Fo CSV /NH 2^>NUL') Do Set "MemKB=%%H"^
& SetLocal EnableDelayedExpansion & For /F %%I In ("!MemKB:,=!") Do EndLocal^
& Set "MemKB=%%I"
Echo %%SvcName%% = %SvcName%, %%PID%% = %PID%, %%MemKB%% = %MemKB% & Pause
You would obviously replace the last line which I've included just to show you the defined variables. I have made it simple to just replace the Service Name on line two, should you wish to do so, but nothing else should be modified at all.
BTW, I will not be explaining how any of the above works, so I advise that you perform further reasearch should you feel the need to understand it, but cannot currently do so.

Get PID from tasklist command

I am using tasklist to bring me information about a specific service/proccess running on my Windows Server.
The command:
tasklist /svc /fi "SERVICES eq .Service02"
The output:
Image Name PID Services
================== ======== ============================================
app02.exe 15668 .Service02
I searched for quite a while now here on StackOverflow, other forums and also on Windows Docs but I couldn't figure out how to get the desired output, which is:
15668
I managed to do a command that kind of worked but not really...
for /f "tokens=1,2 delims= " %A in ('tasklist /svc /fi "SERVICES eq .Service02"') do echo %B
This did not give me the desired output - Instead, it gave me the following output:
C:\Users\admin>echo Name
Name
C:\Users\admin>echo ========
========
C:\Users\admin>echo 15668
15668
If I could only do something that only echoed the third line. The output would be exactly what I need. The PID.
So, I need a command that brings the name of the proccess being used by the service I provide, and return me only its PID.
Please, can someone help me?
Edit: Thanks to #Squashman I managed to do a new command:
tasklist /svc /fi "SERVICES eq .Service02" /FO csv /NH
"service02.exe","15668",".Service02"
And now the output is:
"service02.exe","15668",".Service02"
But where do I go from here?
Just use a for /F loop to capture the CSV output of the tasklist command and to extract the right token.
In Command Prompt:
#for /F "tokens=2 delims=," %P in ('tasklist /SVC /FI "Services eq .Service02" /FO CSV /NH') do #echo %~P
In a batch file:
#echo off
for /F "tokens=2 delims=," %%P in ('
tasklist /SVC /FI "Services eq .Service02" /FO CSV /NH
') do echo %%~P
The ~-modifier removes the surrounding quotation marks from the PID value.
You could of course retrieve the PID using the Service Control executable, sc.exe instead.
#For /F "Tokens=3" %%G In ('%SystemRoot%\System32\sc.exe QueryEx .Service02 ^| %SystemRoot%\System32\find.exe "PID" 2^>NUL') Do #Set "PID=%%G"
However, based upon your reply in comment, here's a quick example to show you how you may be able to perform the task without any need for retrieving the PID:
#Set "SvcName=.Service02"
#Set "SysDir=%SystemRoot%\System32"
#Rem Stop service if memory usage is greater than or equal to 150 MB
#%SysDir%\tasklist.exe /Fi "Services Eq %SvcName%" /Fi "MemUsage GE 153600" /Fo CSV /NH /Svc | %SysDir%\findstr.exe /I /R ",\"%SvcName%\"$" 1>NUL && (
%SysDir%\sc.exe Stop %SvcName%
Rem Add a delay to give the service time to stop
%SysDir%\timeout.exe /T 5 /NoBreak 1>NUL
Rem If service state is stopped then start service again
%SysDir%\sc.exe Query %SvcName% | %SysDir%\findstr.exe /R /C:"STATE *: 1 " 1>NUL && %SysDir%\sc.exe Start %SvcName%)
Line 7 can be adjusted to increase the timeout period from 5 seconds as needed.

Batch error "( unexpected" if statement nested inside for loop

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)

How to identify IMAGENAME with spaces in a batch file (tasklist)?

Right now, I'm writing a batch file with a line that is identifying if a process is running from my process list.
The line I'm referring to:
FOR /F %%x IN ('tasklist /NH /FI "IMAGENAME eq %EXE%"') DO IF %%x == %EXE% goto ProcessFound
EXE is defined beforehand as EXE= My Process Here.exe
My batch file works with normal processes, but as you can see with My Process Here.exe, there is a space between My and Process and Here.exe and this is not recognizable.
Is there any way to fix this? The process I am looking for has spaces and I can't change the process name as the program it is related to will not run if I do.
Thanks.
#ECHO OFF
SETLOCAL
SET "exe=7+ Taskbar Tweaker.exe"
FOR /F %%x IN ('tasklist ^|FINDSTR /i /b /L /c:"%EXE%"') DO IF NOT ERRORLEVEL 1 goto ProcessFound1
ECHO "%exe%" not found
GOTO again
:Processfound1
ECHO "%exe%" found!
:again
SET "exe=I dont exist.exe"
FOR /F %%x IN ('tasklist ^|FINDSTR /i /b /L /c:"%EXE%"') DO IF NOT ERRORLEVEL 1 goto ProcessFound1
ECHO "%exe%" not found
GOTO :eof
:Processfound2
ECHO "%exe%" found!
GOTO :EOF
This may work for you. You'd need to fix the process names of course.
#echo off
setlocal enableextensions disabledelayedexpansion
set "exe=My Process Here.exe"
set "processFound="
for /f "tokens=5 delims=," %%a in ('
tasklist /fi "imagename eq %exe%" /fo:csv /nh
') do set "processFound=1"
if defined processFound (
echo %exe% is running
) else (
echo %exe% is NOT running
)
The tasklist will retrieve the list of processes for for the indicated image name and in csv format, without headers.
There are two options: the process is not running and then there are not csv records in the output, or the process is running and there are csv records in the ouptut.
The for command will try to tokenize the output of tasklist and retrieve the 5th token in the line using commas as delimiters.
If the output from tasklist is a csv record, this token will exist, the replaceable parameter will get data and the code in the do clause will be executed. If the output is not a csv record, the token will not exist, and the code in the do clause will not be executed.
Neither of the prior answers worked for me for identifying imagenames with embedded spaces. What did work for me was to use "delims=tab" where tab is the actual tab character as in:
FOR /F "delims=tab" %%X IN ('tasklist /NH /FI "IMAGENAME eq !EXE!"') DO (...
The resulting %%X will either be the entire tasklist entry corresponding to the !EXE! imagename, if found, or "INFO: No tasks are running which match the specified criteria", if not found, that can be acted on by something like:
SET TASKLINE=%%X
IF "!TASKLINE!"=="!TASKLINE:No tasks are running=!" (
ECHO !EXE! FOUND IN !TASKLINE!
) ELSE (
ECHO !EXE! NOT FOUND IN !TASKLINE!))

How to get PID of process just started from within a batch file?

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

Resources