Compare the three scripts below:
Sample 1
$a = GPS | Where {$_.ProcessName -Match 'AcroRd32'}
$a
$a.Count
If ($a.Count -Eq 0)
{
Echo "Adobe Reader is Off"
}
Else
{
Echo "Adobe Reader is On"
}
# If Adobe Reader is not running, how come 0 (zero) is not returned?
# This is prettier, should I use it? Or does it slow down performance?
Sample 2
$a = GPS AcroRd32
$a
$a.Count
If ($a.Count -Eq 0)
{
Echo "Adobe Reader is Off"
}
Else
{
Echo "Adobe Reader is On"
}
# If Adobe Reader is not running, how come 0 (zero) is not returned?
# This is uglier, but it doesn't have to pipe any output, so does it have any performance gains?
Sample 3
GPS AcroRd32 | Measure | Select -Expand Count
# 0 (zero) is returned, but with an ugly Error
I guess part of my problem is that I'm treating PowerShell like it's VBS; writing code in this manner/style would usually yield me an integer value of zero and not throw any errors (if Adobe Reader was off, of course). What's the correct PowerShell way of checking that an instance of a program is not running? The performance questions in the comments are secondary to the "PowerShell Way" question.
P.S. To be honest, the Error Message returned by the 3rd Sample wouldn't break anything, it's just ugly, so it's not beyond practical use, so I guess the real problem is that I'm just a sucker for aesthetics =^D
This is a common PowerShell gotcha. A command can return:
- Nothing ~ null (does not have the property
Count
, getting it either gets null or fails)
- A single object (it may have its own property
Count
property, the most confusing case -- can return anything; or may not have it, then getting Count
gets null or fails)
- 2+ object array which has the property
Count
.
The solution is simple. When you really need the count of returned objects use the @()
operator. The result is always an array which has the property Count
.
# this is always an array
$result = @(<command returning something or nothing>)
# this is always a number:
$result.Count
I would suggest you to do something like this:
$count = @(get-process | ?{$_.ProcessName -eq "AcroRd32"}).count
if($count -eq 0){
write-host "Adobe Reader is Off"
} else{
write-host "Adobe Reader is On"
}
What the above does is that it forces the returned objects into an array, so if there are no reader processes running, you will get an empty array and hence its count will be zero. And when you do have processes, you have them in the array, and you get the non-zero count and the above code will work as expected.
Alternative based on Sample2 / Sample3:
$acrobat = gps AcroRd32 -ErrorAction SilentlyContinue
if($acrobat){
write-host "Adobe Reader is On"
} else{
write-host "Adobe Reader is Off"
}
In the above, we suppress the error if the reader is not running. Then, if $acrobat is set, you know that the reader is running.
Observations on your code:
When the reader is not running, $a gets assigned nothing and hence $a.Count -eq 0
will be false. When reader is running, $a gets assigned those process objects and you get $a.Count as 1 or more and hence again false. So you will always get that the reader is on.
you mention the error in the third example - you can hide the message by using the SilentlyContinue ErrorAction:
GPS -ea silentlycontinue AcroRd32 | Measure | Select -Expand Count