Lasagna Cat
Lasagna Cat

Reputation: 397

Checking for process and process status in PowerShell version 2 and getting two different outputs depending on single process or multiple processes?

Here is the code below I am using:

$ProcessesToCheckFor = (
    'company_name_uat-Historian'
    )

$FoundProcesses = Get-Process -Name $ProcessesToCheckFor -ErrorAction SilentlyContinue

foreach ($Item in $ProcessesToCheckFor)
    {
    if ($FoundProcesses.Name -contains $Item)
        {
        '{0} runn1ng' -f $Item
        }
        else
        {
        '{0} fai1ed' -f $Item
        }
    }

The code checks to see if company_name_uat-Historian process is running on a server, and if it is running, it will output runn1ng and fai1ed if not.

The problem is, it works when checking only one process just like the code above, but not when you try to check a list of processes.

I need to check a list of processes, so when I chain the rest as listed below:

$ProcessesToCheckFor = (
'company_name_uat-Historian',
'company_name_uat-KEReviewCollector',
'company_name_uat-lwm',
'company_name_uat-MQAck',
'company_name_uat-MQOutput',
'company_name_uat-SQAC',
'company_name_uat-Store',
'company_name_uat-Store_STS',
'company_name_uat-StoreLegacy',
'spotify'
    )

$FoundProcesses = Get-Process -Name $ProcessesToCheckFor -ErrorAction SilentlyContinue

foreach ($Item in $ProcessesToCheckFor)
    {
    if ($FoundProcesses.Name -contains $Item)
        {
        '{0} runn1ng' -f $Item
        }
        else
        {
        '{0} fai1ed' -f $Item
        }

They all output fai1ed.

If I do them one by one, each different process will return runn1ng, just all bunched up together they return fai1ed.

Side notes (if anyone is wondering):

It appears to happen in PowerShell version 2.

Any ideas what is causing it and how I can rewrite it?

Upvotes: 3

Views: 2632

Answers (2)

mklement0
mklement0

Reputation: 439337

Patrick Meinecke's answer is effective and helpful, but let me attempt a more detailed explanation and a more efficient solution:

First things first: Your code works just fine in PSv3+, which should provide an incentive to leave PSv2 behind.

PSv3 introduced unified handling of scalars and collections through member(-access) enumeration, which bridged the great scalar/array divide that plagued PSv2-.

Specifically, in PSv3 you needn't worry about whether Get-Process -Name $ProcessesToCheckFor happens to return a single or multiple items: in either case you can call .Count on the output to count the number of items returned and you can invoke a member (property) on the result, which, in the case of an array result (multiple items) means that the member is called on each element (e.g., .Name), and the result can be again be used as a scalar or array as needed.

In PSv2-, however, you need to be aware of whether a cmdlet - situationally - happens to return a single item or multiple ones, which means:

  • Use array-subexpression operator @(...) to ensure that even if the enclosed command happens to return a single item (scalar), the result is an array.

  • To collect the property values of the elements of a given array or of a scalar in a new array or scalar, use ... | Select-Object -ExpandProperty - again, apply @(...) to ensure that the result is an array.

  • Note that PSv2- had some unified scalar/array handling, but only with respect to pipeline processing: to send a scalar (single item) to the pipeline, you can use it as-is, without needing to wrap it in @(...):
    'foo' | ... works just fine - no need for @('foo') | ... (even though that works too).

Applied to your code we get:

$ProcessesToCheckFor = (
'company_name_uat-Historian',
'company_name_uat-KEReviewCollector',
'company_name_uat-lwm',
'company_name_uat-MQAck',
'company_name_uat-MQOutput',
'company_name_uat-SQAC',
'company_name_uat-Store',
'company_name_uat-Store_STS',
'company_name_uat-StoreLegacy',
'spotify'
)

# Collect the names of all running processes and 
# make sure the result is an array (`@(...)`).
$FoundProcesses = @(Get-Process -Name $ProcessesToCheckFor -ErrorAction SilentlyContinue |
  Select-Object -ExpandProperty Name)

foreach ($Item in $ProcessesToCheckFor) {
  if ($FoundProcesses -contains $Item) {
    '{0} runn1ng' -f $Item
  } else {
    '{0} fai1ed' -f $Item
  }
}

Upvotes: 2

Patrick Meinecke
Patrick Meinecke

Reputation: 4173

The Answer

if (($FoundProcesses | Select-Object -ExpandProperty Name) -contains $Item) {

Why

PowerShell 3 added something called member enumeration. If you have an array of objects in 2.0, you can't directly call properties of the array, because it looks for those properties on the array object itself. In 3.0+ if the member doesn't exist on the array, it will check the items for the member as well. Using Select-Object -ExpandProperty is a more explicit way of calling the members.

You could also just move the Get-Process call into the foreach loop.

foreach ($Item in $ProcessesToCheckFor)
    {
    if (Get-Process -Name $Item -ErrorAction SilentlyContinue)
        {
        '{0} runn1ng' -f $Item
        }
        else
        {
        '{0} fai1ed' -f $Item
        }
    }

Upvotes: 5

Related Questions