1

Тема: CMD/BAT: Определение Process ID текущего cmd.exe

Среди многообразия командных процессоров cmd.exe особо выделяется бедностью и скупостью программных средств. Самоидентификация, или предоставление информации о себе - одно из них. В данном случае речь идет о PID или ProcessID, идентификаторе процесса. cmd.exe не может дать свой PID.

И так! Пафосное вступление закончилось. А теперь суть.

Зачем может понадобиться PID? Это уникальное число в данный момент времени.

-- С помощью PID просто сослаться именно на свой процесс (утилита tasklist ненадежна - достаточно запустить две копии cmd.exe и вы уже не знаете кто из них кто).
-- Может быть удобно при создании временных файлов, каталогов (использование переменной %RANDOM% в качестве части имени файла приводит к конфликтам - CMD/BAT: Проблема создания уникальных значений в конвейерных командах)

Предлагаю небольшой скриптик cmdpid.bat, который помогает идентифицировать PID текущей (!!!) копии cmd.exe. Найденное значение записывается в переменную окружения CMDPID. Функционал реализован на powershell. Суть скрипта в 4 строках после метки :cmdpid, которые можно смело скопировать в свой скрипт; все остальные - суть обвязка для удобства использования в качестве самостоятельной утилиты.


@echo off

if "%~1" == "/?" goto :help
if "%~1" == "-?" goto :help

if /i "%~1" == "/h" goto :help
if /i "%~1" == "-h" goto :help


for %%p in ( powershell.exe ) do if "%%~$PATH:p" == "" (
    >&2 echo:%%p required.
    exit /b 1
)


:cmdpid
for /f "tokens=*" %%p in ( '
    set "PPID=(Get-WmiObject Win32_Process -Filter ProcessId=$P).ParentProcessId" ^& ^
    call powershell -NoLogo -NoProfile -Command "$P = $pid; $P = %%PPID%%; %%PPID%%"
' ) do set CMDPID=%%p

goto :EOF


:help
echo:Calculates the Process ID of the Command Prompt 
echo:and assigns it to the CMDPID variable.
echo:
echo:Usage: %~n0
( 2 * b ) || ! ( 2 * b )

2

Re: CMD/BAT: Определение Process ID текущего cmd.exe

Rumata, как вариант:

@for /f %%i in ('set "x=%~f0"^& call wmic process where "CommandLine like '%%%%x:\=\\%%%%'" get ProcessId^| findstr [0-9]') do @set PID=%%i& call echo %%PID%%& pause>nul& exit /b

.

3

Re: CMD/BAT: Определение Process ID текущего cmd.exe

Yury, Ваш вариант с использованием WMIC работает. Однако он не дает гарантии, что в данный момент запущен единственный экземпляр скрипта, например в конвейере.

( 2 * b ) || ! ( 2 * b )

4

Re: CMD/BAT: Определение Process ID текущего cmd.exe

А Ваш, нужно, полагать, дает? И потом, зачем WMI, когда есть счетчики производительности? http://powershell.com/cs/emoticons/emotion-8.gif

(New-Object Diagnostics.PerformanceCounter('Process', 'Creating Process ID', 'cmd')).RawValue

5

Re: CMD/BAT: Определение Process ID текущего cmd.exe

Если бы Вы предложили эдакий хак с typeperf и tasklist - другое дело, в противном случае это уже из области Managed CMD\BAT.

6

Re: CMD/BAT: Определение Process ID текущего cmd.exe

Между прочим, способ не универсальный: что, если пользователь привык работать с урезанными правами в системе?

7

Re: CMD/BAT: Определение Process ID текущего cmd.exe

Rumata, у меня есть большое подозрение, что "WMIC" сортирует одноимённые процессы по их дате создания, начиная со старшей, поэтому, если это подтвердится, то в моём коде, например, достаточно поставить скобки, чтобы получить гарантию, что полученный "PID" относится к текущему процессу, а не к процессу, запущенному ранее:

@(for /f %%i in ('set "x=%~f0"^& call wmic process where "CommandLine like '%%%%x:\=\\%%%%'" get ProcessId^| findstr [0-9]') do @set PID=%%i)& call echo %%PID%%& pause>nul& exit /b

.

8

Re: CMD/BAT: Определение Process ID текущего cmd.exe

Yury
запустите следующий код в виде

script | script

или даже

script & script

чтобы увидеть, что время создания процессов в подавляющем большинстве случаев одинаково.


@wmic path win32_localtime get second /value >&2

greg zakharov

есть счетчики производительности

я не сомневаюсь в Вашей компетентности в powershell. Однако мне не понятно как счетчики производительности могут помочь в определении PID текущего экземпляра cmd.exe.

что, если пользователь привык работать с урезанными правами в системе?

Не могу ответить. У меня нет такой возможности.

( 2 * b ) || ! ( 2 * b )

9

Re: CMD/BAT: Определение Process ID текущего cmd.exe

Rumata, да, время одинаковое, но у Вас речь идёт о секундах. А вот так всегда разное:

@wmic os get LocalDateTime /value

.
А ведь "WMIC" при сортировке ориентируется именно на этот формат.

10

Re: CMD/BAT: Определение Process ID текущего cmd.exe

Однако мне не понятно как счетчики производительности могут помочь в определении PID текущего экземпляра cmd.exe.

Скорее PPID.

Не могу ответить. У меня нет такой возможности.

Тогда проблема на порядок выше, чем кажется.

11 (изменено: Rumata, 2014-12-21 14:46:37)

Re: CMD/BAT: Определение Process ID текущего cmd.exe

Yury
Видимо Вы не видите проблему, о которой я говорю. Запустите свой скрипт в конвейере. У Вас почти всегда будет одинаковое время.

greg zakharov

Тогда ...

Коллега, не могли бы Вы выражать свои мысли яснее? Короткие фразы в контексте Ваших мыслей сложно понять в контексте тематической дискуссии.

( 2 * b ) || ! ( 2 * b )

12 (изменено: Poltergeyst, 2023-10-29 00:42:04)

Re: CMD/BAT: Определение Process ID текущего cmd.exe

Можно попробовать передавать идентификатор процесса cmd через ERRORLEVEL:
(Win7)

cpid.cmd


@set @q=1/*

 :: ---------------------------------
 :: Идентификатор текущего процесса CMD
 :: (возврат значения через ERRORLEVEL)
 :: ---------------------------------

 @echo off
 setlocal enableextensions enabledelayedexpansion

 cscript /nologo /e:jscript "%~f0"

 set pid=%ERRORLEVEL%
 echo PID: %pid%

 pause
 goto :eof
 
*/

 var wql = "winmgmts:\\\\.\\root\\cimv2:win32_process.Handle=";

 var oProc = GetObject(wql + new ActiveXObject("WScript.Shell").Exec("rundll32 kernel32,Sleep").ProcessId);
 PID_WSH = oProc.ParentProcessId;
 oProc.Terminate;

 var oProc = GetObject(wql + PID_WSH);
 PID_CMD = oProc.ParentProcessId;

 WScript.Quit(PID_CMD);

CMD/BAT: возврат значений из процедур через параметры
WSH: как из скрипта определить его идентификатор процесса (PID)

С применением утилиты winapiexec:


 :: ---------------------------------
 :: Идентификатор текущего процесса CMD
 :: (используется winapiexec.exe)
 :: (возврат значения через ERRORLEVEL)
 :: (переменная ERRORLEVEL возвращается через функцию system)
 :: ---------------------------------

 @echo off
 setlocal enableextensions enabledelayedexpansion

 set pid0=%1
 if "%pid0:~1,7%"=="initpid" goto scr2
 
 winapiexec.exe k@GetCurrentProcessId , k@AttachConsole -1 , msvcrt.dll@sprintf $b:255 $s:"cmd /c "%~f0" /initpid:%%u" $$:1 , msvcrt.dll@system $$:7 , k@FreeConsole , k@ExitProcess $$:11

 set pid=%ERRORLEVEL%
 echo PID: %pid%

 pause
 exit /b 0
 
 :: ---------------------------------
 :scr2
 set pid0=%pid0:/initpid:=%

 for /f "usebackq tokens=2 delims==" %%b in (`wmic process where ^(processid^=%pid0%^) GET parentprocessid /format:VALUE`) do set pid1=%%b
 exit /b %pid1%

С применением LangMF 11:


 :: ---------------------------------
 :: Идентификатор текущего процесса CMD
 :: (LangMF 11)
 :: (возврат значения через ERRORLEVEL)
 :: ---------------------------------

 @echo off
 setlocal enableextensions enabledelayedexpansion

 "C:\Progra~1\LangMF\LangMF_stand.exe" $ sTxt="winmgmts:\\.\Root\CIMV2:Win32_Process.Handle=PID" : lPID1=DllCall("KERNEL32.DLL","GetCurrentProcessId") : sTxt=~igm/"PID"/lPID1/ : lPID2=GetObject(sTxt).ParentProcessId : r=DllCall("KERNEL32.DLL","ExitProcess",lPID2)

 set pid=%ERRORLEVEL%
 echo PID: %pid%

 pause
 exit /b 0