E235
E235

Reputation: 13500

How to get process id by its service name with a script to variable

I have service named WinDefend and it runs on process svchost.exe
There other many svchost.exe processes and I need to find a way to get its ID.
when I run tasklist /svc I can see: enter image description here

I am not sure how can I get it.
I found this command but when I tried the select "PID" it gave me empty column. enter image description here

I need to get the PID of the process to variable.

Upvotes: 25

Views: 118204

Answers (10)

Jay M
Jay M

Reputation: 4327

Here is a slightly expanded answer.

It goes beyond the original question but I though it worth adding as I came here with the same question but with awareness that the answer in my case would be an array rather than a single value. Obviously this is still valid for an array of just one value, or can be modified to return less than the full data set.

I had to do this as I needed a list of all svchost PIDs together with their command lines. This was to investigate a Windows Firewall issue.

I am new to PowerShell (and TBH in two minds about it) I am sure a person more versed in it than I am can think up better methods. Anyway after reading the answers above, (and with a little playing), I came up with this; Which delivered exactly what I needed:

# Block #1
$ServiceList = ((Get-WmiObject -Class Win32_Service).ProcessId -ne 0) `
  | ForEach-Object {
    Get-Process -Id $_ | Where-Object -Property Name -eq "svchost" `
    | SelectObject -Property Id,Name,CommandLine `
  })

By selecting a list of PIDs first, we can ignore the need for Get-Service as only Running and Enabled services will have a PID that is non-zero. Using objects means we can use get-service later if required.

Now we have it as an array variable of objects we can further manipulate it. Indeed we could have omitted the filter on service name and included it below.

# Block #2
$ServiceList | Sort-Object -Property Id | Format-Table -AutoSize -HideTableHeaders

By the way another way is to use the $obj.where() method instead of filtering as we collect the data, we run a filter as we process it. e.g.

# Block #3
# Omitting the `| Where-Object filter`, in #1, above and using this instead of #2
$ServiceList.where({$_.Name -eq "svchost"}) | Sort-Object -Property Id | ConvertTo-csv
# As an alternate example, the above converts to a useful M2M format
# (e.g. csv or json) rather than a pretty printed table intended for humans.
# This might save a lot of dodgy screen scraping!

I can use this against the Firewall log which only includes the PID of the process that was blocked. Now this can be sorted, filtered and searched via code or by simply using excel, which is what worked for me. NOTE: As some will be aware, svchost implements a whole stack of services, which look like they all have the same name, so the command name alone does not tell us what entry in the firewall table.

For the sake of completeness, one last improvement I realised was to create a sort of custom object that contains attributes from both the service and the associated process. I use the ForEach method, rather than the filter or statement (all with the same name, hmm) as it seemed to be more concise and flexible.

# Block #4
# In place of #3 - Again delegating the filtering to another code block.
$ServiceList.ForEach({ $_ | Add-Member -MemberType "NoteProperty" -Name "ProcessName" -Value (Get-Process -Id $_.ProcessId).Name -ErrorAction SilentlyContinue})
# NOTE: The ErrorAction prevents missing process Name attributes throwing an error and breaking the iteration.

# Note: In this case we are filtering to select all the processes (which may have different names) with the same single service name. As all the services are implemented in the same executable (`svchost.exe`), we can omit that attribute as all the rows in output for that field/column will be the same.

$ServiceList.Where({ $_.ProcessName -eq "svchost"} ) | Select-Object -ExcludeProperty ServiceName | ...... then Sort-Object or Convert as before....

Upvotes: 0

ITSteve
ITSteve

Reputation: 1

While the WMI option is good, I wanted something in a single PS line so I could easily insert it into the rather cludgy scripting toolset of the RMM tool my company uses.

What I came up with was:

(tasklist /FI "Services eq WinDefend"/FO LIST | findstr /B "PID:") -replace "[^0-9]" , ''

You could take it to the conclusion I did by then wrapping it into a taskkill command:

taskkill /f /pid ((tasklist /FI "Services eq WinDefend"/FO LIST | findstr /B "PID:") -replace "[^0-9]" , '')

Upvotes: 0

Ronald Damalerio
Ronald Damalerio

Reputation: 1

The answer is simple. (gps -id (get-wmiobject -query "select * from win32_service where name='sevicename'").processid).priority class="priority"

Do that command and it will set the specified service even it's in svchost.

Edited: You are only looking for the PID. just do the same command minus the priority setting. I was assuming you are going to use it to set priority. Hehe

Upvotes: 0

Mike Zboray
Mike Zboray

Reputation: 40838

tasklist is just returning text, not actual objects that have properties you can access. You can use WMI to get this information instead:

$id = Get-WmiObject -Class Win32_Service -Filter "Name LIKE 'WinDefend'" | 
      Select-Object -ExpandProperty ProcessId

$process = Get-Process -Id $id

Update for PowerShell Core

In version 6, Windows PowerShell started towards cross platform support with PowerShell Core based on .NET Core. This led to many changes in cmdlets that were Windows-centric and some being left out completely. WMI is a Windows only technology, so its cmdlets (e.g. Get-WmiObject) were not ported over. However, its features are available via CIM cmdlets (e.g. Get-CimInstance) here is a version that will work on PowerShell 6+:

$id = Get-CimInstance -Class Win32_Service -Filter "Name LIKE 'WinDefend'" | 
      Select-Object -ExpandProperty ProcessId

$process = Get-Process -Id $id

Upvotes: 45

Bartosz X
Bartosz X

Reputation: 2798

Get-Process -Id ((Get-WmiObject -Class Win32_Service -Filter "Name -eq 'WinDefend'").ProcessId)

Upvotes: 0

WebTek
WebTek

Reputation: 17

I have used this and it works fine. YMMV

$ProcessName = "SomeProcessName"

$pidnumber = Get-Process -Name $ProcessName | Select -expand ID

Upvotes: -1

Kambo
Kambo

Reputation: 21

# Enter servicename. (instead of 'netman')
$service = Get-CimInstance -class win32_service | Where-Object name -eq 'netman' | select name, processid
$process = Get-Process | Where-Object ID -EQ $service.processid
Clear-Host
Write-Host '********* ServiceName, PID and ProcessName ******'
Write-Host 'ServiceName:' $service.name 
Write-Host 'ID:' $process.Id 
Write-Host 'ProcessName:' $process.Name

Thanks,

Upvotes: 2

eFox
eFox

Reputation: 695

An alternative way to get a process PID:

$serviceName = 'svchost.exe'
$pidArgumentPlacement = 1

# Call for the verbose version of tasklist and filter it for the line with your service's name. 
$serviceAsCSVString = tasklist /v /fo csv | findstr /i $serviceName

# Remove the quotes from the CSV string
$serviceCSVStringWithoutQuotes = $serviceAsCSVString -replace '["]'
# Turn the string into an array by cutting at the comma
$serviceAsArray = $serviceCSVStringWithoutQuotes -split ","
# Get the pid from the array
$servicePID = $serviceAsArray[$pidArgumentPlacement]

Or you can sum it up to:

$servicePID = $($($(tasklist /v /fo csv | findstr /i $serviceName) -replace '["]') -split ",")[$pidArgumentPlacement]

Note: This will grab the first service that matches your $serviceName, if you run a service that runs several instances of itself (e.x. slack) you'll only get the first pid. tasklist /v /fi "IMAGENAME eq slack.exe" /fo csv will return an array with each CSV line being an array entry. You can also filter this with findstr to avoid getting the column names.

EDIT: As WinDefend is a subservice of a program (In this case svchost.exe) you may need to swap the verbose flag for tasklist to /svc like so:

$serviceAsCSVString = tasklist /svc /fo csv | findstr /i $serviceName

alternatively search for the service's name through a filter:

$serviceAsCSVString = tasklist /svc /fi "SERVICES eq $serviceName" /fo csv | findstr /i $serviceName

And taking into account that the filter returns a row of column names as well as the line you were looking for:

$serviceCSVStringWithoutQuotes = $serviceAsCSVString[1] -replace '["]'

Assuming you've changed $serviceName to WinDefend instead of svchost.exe.

Upvotes: 0

Mark Ronollo
Mark Ronollo

Reputation: 189

Annoying as this is, it requires you to set a unique title for your script if you want the pid for the current process. Then search for that unique title within the list of processes. Thankfully, the Title command allows you to do just that. Also see MagicAndi's response...

Here is my batch file solution:

@ECHO OFF
:SetVars
    SET _Thread=%1
    title=ExecBatch_%_Thread%
    Set /A "_iPID=0"
:Main
    CALL :getPID _iPID %_Thread%
    ...
EXIT /b

::----------------
::---- GetPID ---- 
::----------------
:getPID 
    setlocal   
        set _getPIDcmd=tasklist /v /fo csv 
        for /f "tokens=2 delims=," %%i in ('%_getPIDcmd% ^| findstr /i "ExecBatch_%2"') do (
            echo %%~i
            set _pid=%%~i
        )
    endlocal & Set %~1=%_pid%
exit /b

BTW, I've had the 'pleasure' of doing this time and time again over the years, via API, or batch, or ps. Pick your poison - on a Windows platform it's all the same.

I found an even better way via powershell: $pid returns the current process' process id.

Upvotes: 3

walid toumi
walid toumi

Reputation: 2282

$p=Tasklist /svc /fi "SERVICES eq windefend" /fo csv | convertfrom-csv
$p.PID

Upvotes: 9

Related Questions