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

10

25

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.

Zig answered 12/10, 2014 at 23:47 Comment(0)
B
43

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
Bedell answered 13/10, 2014 at 0:0 Comment(2)
I am sorry for the very dumb question but where do i execute this?Lavone
@RaulChiarella In powershell? :) I'm not sure quite what you mean, but with powershell core being the standard now I've made an update that should work with that version.Bedell
M
8
$p=Tasklist /svc /fi "SERVICES eq windefend" /fo csv | convertfrom-csv
$p.PID
Mireielle answered 13/10, 2014 at 1:47 Comment(0)
A
3

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.

Atmo answered 16/8, 2016 at 20:53 Comment(1)
I think you misunderstood the question by a lot.Echelon
B
2
# 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,

Biffin answered 25/8, 2018 at 13:51 Comment(1)
While this answer may solve the question it would be much more useful if you explained how it worksAdolfo
A
0

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.

Acculturation answered 9/11, 2017 at 12:18 Comment(4)
When I run $servicename = 'WinDefend' $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 the $serviceAsCSVString is empty.Zig
Sorry, there's a typo in my script; $serviceName was misspelled on the first line. Try it again and make sure the variable names match; powershell is case sensitive.Acculturation
It sitll doesn't work and Powershell is not case sensitive. If you will run $a = "a"; $A you will see that it will print the same value. Did you check this code on your machine ? Run just: $serviceName = 'WinDefend' $pidArgumentPlacement = 1 tasklist /v /fo csv | findstr /i $serviceName It won't find anything. The problem is because you are not using the /svc switch that displays the service name. Therefore it doesn't display the WinDefend service. Fix for your code: Just replace /v to /svc Btw svchost.exe is not a good example because it is not a service.Zig
I've updated my Answer, it seems that I was missunderstanding what you were looking for.Acculturation
B
0
Get-Process -Id ((Get-WmiObject -Class Win32_Service -Filter "Name -eq 'WinDefend'").ProcessId)
Baerl answered 27/4, 2021 at 14:7 Comment(1)
While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value.Levulose
W
0

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

Wheatworm answered 1/1, 2022 at 10:18 Comment(0)
N
0

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]" , '')
Nyhagen answered 23/10, 2023 at 13:2 Comment(0)
P
0

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....

Parochialism answered 27/4 at 1:28 Comment(0)
F
-1

I have used this and it works fine. YMMV

$ProcessName = "SomeProcessName"

$pidnumber = Get-Process -Name $ProcessName | Select -expand ID
Fania answered 1/11, 2018 at 16:51 Comment(3)
But my case is different. The process name is svchost.exe which appears couple of times in the task manager. Then, you need to find only the one that is related to the service of the WinDefend" and just showing the IDs of all the svchost.exe won't solve itZig
I'm trying to do this on a service, I can't seem to use the -expand id with get-service.Branum
@Branum that's because the service name will not be necessarily the same as the process name. This is a pretty bad answer in response to the question.Unaffected

© 2022 - 2024 — McMap. All rights reserved.