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