1

This should be quite simple but I didn`t manage to find it.

If I am running some batch script in cmd, how do I get process id of the hosting cmd?

set currentCmdPid= /* some magic */
echo %currentCmdPid%

I have found a lot of solutions that use tasklist and name filtering but I believe that this won`t work for me, since there can be launched many instances of cmd.

Again, I`d like to have some simple elegant and bullet-proof solution.

Thanks.

steavy
  • 1,483
  • 6
  • 19
  • 42
  • Well, it's not simple, but I think [it is bullet-proof](http://stackoverflow.com/a/27514649/1683264) anyway. – rojo Mar 04 '15 at 18:29
  • Why is this so hard on Windwos :( It`s such a simple and common thing to do. – steavy Mar 04 '15 at 18:31
  • It would be nice if the cmd environment were exposed to its own process info via environment variables, wouldn't it? Anyway, what's your purpose in getting the current console's PID? If you're using it for process locking, [there are probably better solutions for that](http://stackoverflow.com/a/27756667/1683264). – rojo Mar 04 '15 at 18:39
  • No, I need to run some application in the cmd and store the pid into file for later use. Especially to kill the application at some later point of time. – steavy Mar 04 '15 at 18:52
  • 3
    Use [dbenham's solution](http://www.dostips.com/forum/viewtopic.php?p=38870#p38870) to find the PID of `cmd.exe`. It's faster than npocmaka's or mine. npocmaka still gets a +1 for pointing me to dbenham's. :) If you want to capture the PID of the process you're launching, rather than the PID of `cmd` itself, [see this page](http://stackoverflow.com/questions/28853775/). – rojo Mar 04 '15 at 18:56
  • Your spawn script seems to be working and returns the correct Pid. And probably the last question about usage. What if I need to redirect the output of the application that I launch. By simply doing this `call :spawn app.exe 1>log.txt` it doesn`t redirect – steavy Mar 04 '15 at 19:13
  • 1
    Easiest solution would be to `call :spawn wrapper.bat` and have the contents of wrapper.bat as `@app.exe >path\to\log.txt`. Or you could hard code `wmic process call create "%args:"=\"% >path\\to\\log.txt"`. Either way, you must fully qualify the `c:\path\to\log.txt`. I think `wmic process call create` uses `%systemroot%\system32` for its working directory. I'm not sure how to pass `>` as part of a `call` argument without quoting it, so I can't advise you on how to include the redirect on the same line as `call`. Escaping the `>` with a caret doesn't seem to have any effect in my testing. – rojo Mar 04 '15 at 20:01

3 Answers3

3

Here is my pure batch script version that resulted from the same DosTips discussion and collaborative effort that npocmaka referenced.

@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

npocmaka's solution installs an exe file (compiled JScript) in the same folder as the batch script.

On my machine, my pure batch getPID runs 20% faster than npocmaka's exe! This is because the exe first determines its own process PID, which takes significant time, before it uses WMI to determine the desired parent PID. My script generates a unique ID (much faster) which gets incorporated into the WMIC call that determines the desired parent PID.

In both solutions, the bulk of the time is spent within WMI where it determines the parent PID of a specific process.

Community
  • 1
  • 1
dbenham
  • 127,446
  • 28
  • 251
  • 390
2

Here the topic has been discussed -> http://www.dostips.com/forum/viewtopic.php?p=38870

Here's my solution:

@if (@X)==(@Y) @end /* JScript comment
@echo off
setlocal

for /f "tokens=* delims=" %%v in ('dir /b /s /a:-d  /o:-n "%SystemRoot%\Microsoft.NET\Framework\*jsc.exe"') do (
   set "jsc=%%v"
)


if not exist "%~n0.exe" (
   "%jsc%" /nologo /out:"%~n0.exe" "%~dpsfnx0" 
)

%~n0.exe

endlocal & exit /b %errorlevel%

*/


import  System;
import  System.Diagnostics;
import  System.ComponentModel;
import  System.Management;

var myId = Process.GetCurrentProcess().Id;
var query = String.Format("SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = {0}", myId);
var search = new ManagementObjectSearcher("root\\CIMV2", query);
var results = search.Get().GetEnumerator();
if (!results.MoveNext()) {
   Console.WriteLine("Error");
   Environment.Exit(-1);
}
var queryObj = results.Current;
var parentId = queryObj["ParentProcessId"];
var parent = Process.GetProcessById(parentId);
Console.WriteLine(parent.Id);
npocmaka
  • 55,367
  • 18
  • 148
  • 187
  • Please quote the essential parts of the answer from the reference link(s), as the answer can become invalid if the linked page(s) change. – DavidPostill Mar 05 '15 at 11:48
0

To get cmd PID, from cmd I run powershell and get parent process Id -- that will be cmd PID. Script gets parent of a parent (since FOR /F calls another cmd.exe).

FOR /F "tokens=* USEBACKQ" %%F IN (`powershell  -c "(gwmi win32_process | ? processid -eq ((gwmi win32_process | ? processid -eq  $PID).parentprocessid)).parentprocessid"`) DO (
  SET PCT_CleanAfterPID=%%F
)

ECHO %PCT_CleanAfterPID%
Stanislav Berkov
  • 5,929
  • 2
  • 30
  • 36