39

I'm trying to find a way to get my own PID from a command prompt (for later use in bat scripts).

So far the only useful way I found was to use getpids.exe from here : http://www.scheibli.com/projects/getpids/index.html, but I'm looking for a command that's "built in" to Windows.

I'm looking for a "bullet proof" way. No assumptions about my process being the only cmd.exe or anything.

0

16 Answers 16

52

Since none of the other solutions are bulletproof and built in, I figured I'd offer the following solution, but note that you'll need to parse/save the results somehow:

title mycmd
tasklist /v /fo csv | findstr /i "mycmd"
7
  • 1
    thats really neat. im pretty sure the current time + some randomly-generated number can be set as the title to get near bulletproof
    – radai
    Mar 29, 2010 at 10:49
  • 4
    Don't use %random% + %time% if you have a scheduler call your batch script twice at the same time. I had the issue that the same script was called with different parameters at the same time. The seed for random was the same and I ran into collisions when creating a temporary directory. There was no way around then to handle the error when creating the temporary directory. Long story short, if you can assume that your script does not get fired of twice at the same time, this trick should work. Nov 11, 2014 at 17:05
  • Might be (not deeply tested) that getting a more precise timestamp (including microseconds, rather than 1/100 seconds like in %time%), would be more reliable in this case: wmic os get LocalDateTime (YearMonthDayHourMinuteSecond.Microsecond+Timezone). Unless the scheduler is able to run two things in the exact same microsecond... Dec 15, 2014 at 6:38
  • This won't work if the process is running in another session on the computer as the title is shown as N/A Feb 3, 2016 at 11:19
  • This gives the PID of the parent process. Apr 12, 2017 at 22:48
13

Expanding upon Tony Roth's answer:

title uniqueTitle
for /f "tokens=2 USEBACKQ" %f IN (`tasklist /NH /FI "WINDOWTITLE eq uniqueTitle*"`) Do echo %f

Using the WINDOWTITLE filter avoids the pipe so you can put it in a for loop and assign it to a variable with SET if you like:

title uniqueTitle
for /f "tokens=2 USEBACKQ" %f IN (`tasklist /NH /FI "WINDOWTITLE eq uniqueTitle*"`) Do set ourPID=%f

Removing the /v makes it quicker, and the /NH gets rid of the header line. You need the wildcard after "uniqueTitle" because the window title actually contains the current command (thus it would go on and on if you tried to match it fully).

13

Using PowerShell + WMI :

powershell (Get-WmiObject Win32_Process -Filter ProcessId=$PID).ParentProcessId

Using WMIC :

for /f %a in ('wmic os get LocalDateTime ^| findstr [0-9]') do set NOW=%a
wmic process where "Name='wmic.exe' and CreationDate > '%NOW%'" get ParentProcessId | findstr [0-9]

(as always, double the % sign with for in batch files)

4
  • 1
    Nice; if you have PowerShell Core, it gets even easier (though neither command will be fast): pwsh -noprofile -c "(Get-Process -Id $PID).Parent.Id"
    – mklement
    Oct 6, 2019 at 20:22
  • 1
    Your "Using PowerShell + WMI" -solution is the best! However I noticed that you cannot use it in a FOR -statement, because then the the parent process id the FOR-loop. My suggestion uses tempfile with GUID as the tempfile name: FOR /F "tokens=*" %%G in ('powershell -ExecutionPolicy Unrestricted -Command "(New-Guid).guid"') do Set my_guid=%%~G powershell -ExecutionPolicy Unrestricted -Command "(Get-WmiObject Win32_Process -Filter ProcessId=$PID).ParentProcessId>%temp%\%my_guid%.pid FOR /F "tokens=*" %%P in ('type %temp%\%my_guid%.pid') do set my_pid=%%~P
    – Ciove
    Aug 31, 2021 at 13:19
  • 1
    @Ciove, you can also reach for the parent's parent : for /F %%a in ('powershell -ex bypass "(gwmi Win32_Process -f ProcessID=$((gwmi Win32_Process -f ProcessId=$PID).ParentProcessID)).ParentProcessID"') do set my_pid=%%a (or even for /F %%a in ('powershell -ex bypass "gwmi Win32_Process -f ProcessId=$PID | foreach {gwmi Win32_Process -f ProcessId=$($_.ParentProcessID)} | foreach ParentProcessID"') do set my_pid=%%a, which might be more readable) Jan 28, 2022 at 12:56
  • As you might have noticed, the powershell approach gets the parent process of the powershell subprocess just invoked, which is me, the current process. Who am I? I am my child's parent. Apr 5, 2023 at 22:09
10

I believe the following is bulletproof, provided the user has access to WMIC and TEMP points to a valid path where the user has write privileges. This is the end result of some collaborative work at http://www.dostips.com/forum/viewtopic.php?f=3&t=6133.

@echo off

:getPID  [RtnVar]
::
:: Store the Process ID (PID) of the currently running script in environment variable RtnVar.
:: If called without any argument, then simply write the PID to stdout.
::
setlocal disableDelayedExpansion
:getLock
set "lock=%temp%\%~nx0.%time::=.%.lock"
set "uid=%lock:\=:b%"
set "uid=%uid:,=:c%"
set "uid=%uid:'=:q%"
set "uid=%uid:_=:u%"
setlocal enableDelayedExpansion
set "uid=!uid:%%=:p!"
endlocal & set "uid=%uid%"
2>nul ( 9>"%lock%" (
  for /f "skip=1" %%A in (
    'wmic process where "name='cmd.exe' and CommandLine like '%%<%uid%>%%'" get ParentProcessID'
  ) do for %%B in (%%A) do set "PID=%%B"
  (call )
))||goto :getLock
del "%lock%" 2>nul
endlocal & if "%~1" equ "" (echo(%PID%) else set "%~1=%PID%"
exit /b

The script establishes an exclusive lock on a temporary file that incorporates the current time into the name. There can only be a collision if two like named batch processes attempt to get the PID within the same 0.01 second time interval, in which case only one will succeed.

Any process that fails will repeatedly loop back and try again with a new lock file path until it succeeds.

The full path to the lock file is transformed into a unique ID that can be used in the WMIC query. WMIC is run within a FOR /F command, which means it is running in a child cmd.exe process. That is why the ParentProcessID of the cmd.exe process is retrieved.

1
  • I love looking for a CMD.EXE trick and coming up with an answer from dbenham. I know the code is going to be well-written, meticulous in its handling of edge-cases, and problems I didn't even think of will have been considered and solved. (I know, I know... I shouldn't be writing scripts in CMD.EXE in 2023 either... >sigh< Old man yells at PowerShell, I guess. I have too many battle-hardened scripts from the last 27-ish years that I can't let go of.) Feb 6, 2023 at 19:12
5
  1. Windows Task Manager, you will need to go to View -> Select Columns.. and select PID.
  2. "tasklist /v" to get verbose task information in command prompt.
  3. Process Explorer from live.sysinternals.com.
3
  • 3
    @Vivek, from a command prompt... and you are duping answers
    – WooYek
    Mar 27, 2010 at 0:02
  • i just gave all possible options.. and no duplicating.. i could have also mentioned PowerShell but i restrained myself as that does not comes by default in XP and 2003. If this is for Vista onwards OS, Yes PowerShell would have been my preference. Mar 27, 2010 at 0:24
  • The question contains "... from the command prompt...", and your answers are "Windows Task Manager" (GUI, not command prompt) and "Process Explorer" (GUI, not command prompt). Did you even bother to read the question?
    – sdbbs
    Oct 12, 2021 at 13:19
5

if you know there's only one cmd.exe running, you can get the PID this way:

for /F "tokens=1,2" %%i in ('tasklist /FI "IMAGENAME eq cmd.exe" /fo table /nh') do set pid=%%j

echo %pid%
3
  • 1
    That sounds like an improbable assumption, your code could be flaky Jul 9, 2019 at 6:48
  • Raul, I understand what you're saying. It's unsafe in many cases, but is safe in some -- such as a kiosk system or a VM where you're in total control of what processes are starting and not Jul 9, 2019 at 23:24
  • Even if you know there's only one cmd.exe running the given code supposedly returns wrong value as the tasklist runs in a child cmd instance invoked from FOR command. Proof: for /F "tokens=*" %G in ('tasklist /FI "IMAGENAME eq cmd.exe" /fo table /nh') do @echo %G. I'd guess that this child process is listed down to end so this could help: set "pid="&for /F "tokens=1,2" %%i in ('tasklist /FI "IMAGENAME eq cmd.exe" /fo table /nh') do if not defined pid set pid=%%j
    – JosefZ
    Feb 18, 2020 at 19:18
4

This should do the trick:

tasklist /v

3

If you want to find the PID of imagename "notepad.exe", then the following code will work for you:

for /F "tokens=1,2" %i in ('tasklist') do (
 if "%i" equ "notepad.exe" (set x=%j)
)
echo %x%
1
  • there's no need to parse the process name yourself like that when tasklist already has the same capability
    – phuclv
    Apr 2, 2020 at 1:18
2

If you've got the Windows 2003 Resource kit push it through qgrep to get just the line you want. You could then extract the pid from here (this assumes you've only got one cmd running at a time),

tasklist /v | qgrep cmd

cmd.exe 2040 RDP-Tcp#447 0 1,804 K Running MACHINE\Administrator  0:00:00 Command Prompt
2

Take a look at this little batch trick. It sets the title of cmd to a special value then uses tasklist to find it. Inventive

\\Greg

1

THIS IS SHORT WAY TO GET PROCESS ID FOR OPEN CMD

tasklist /v /fi "imagename EQ cmd.exe" /FO LIST | FIND "PID:"
1

you can try with getcmdPid.bat - this is a selfcompiled .net application embedded in a bat script and returns the PID as the errorlevel:

call getcmdPid
echo %errorlevel%
1
  • This works well for me but it leaves an exe file after retrieving the PID, and it makes me feel a bit of 'virusly', if you know what I mean. Still, a nice trick!
    – 0xAA55
    Nov 5, 2021 at 11:07
0

This answer will give you ONLY the process ID, and none of the extra stuff the top answer includes.

title mycmd
tasklist /v /fo csv | findstr /i "mycmd" > PIDinfo.txt

set /p PIDinfo=<PIDinfo.txt
set PID1=%PIDinfo:~11,5%
set PID2=%PIDinfo:~11,4%

if %PID2% gtr 8100 (
    set PID=%PID2%
) else (
    set PID=%PID1%
)

echo %PID%

Explanation:

-there will not be a PID for cmd.exe that is greater than 18100 so check if PID2 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 PID1 val 17504 and a PID2 val of 1750, so we use PID1

case 2: a 4 digit PID like 8205 has a PID1 val of 8205" and a PID2 val of 8205, so we use PID2

case 3: a 4 digit PID like 4352 has a PID1 val of 4352" and a PID2 val of 4352, so we use PID2

0

To get the PID for the current user, see my example below for restarting the explorer process:

@echo off
for /F "tokens=1,2" %%i in ('tasklist /FI "IMAGENAME eq explorer.exe" /FI "USERNAME eq %USERDOMAIN%\%USERNAME%" /FO table /NH') do taskkill /F /PID %%j
start explorer.exe
0

From the above I used this to get current process id in a batch file: for /f %a in ('powershell "$proc=Get-WmiObject Win32_Process -Filter ProcessId=$PID; $proc.ParentProcessId"') do set PID=%a

1
  • This gives me a different PID every time I run it and it's not 'this' cmd's... The for uses an extra cmd it seems. Nov 23, 2022 at 14:52
0

I found the easiest solution in a comment by Nicolas Melay:

for /F %%a in ('PowerShell -ex bypass "(gwmi Win32_Process -f ProcessID=$((gwmi Win32_Process -f ProcessId=$PID).ParentProcessID)).ParentProcessID"') do set my_pid=%%a

It uses PowerShell to get the PID of the grandparent process, as the for introduces an extra cmd.exe.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .