Powershell - add catch to pick up if there are no Excel processes

我怕爱的太早我们不能终老 提交于 2021-01-27 13:20:41

问题


Is there way to add an if/else catch to only

$excelId = get-process excel | %{$_.Id} | ?{$before -notcontains $_}

if there is no excel process running? e.g. if Excel is running then get-process id, if not then ignore it.

Get-Process : Cannot find a process with the name "excel". Verify the process name and call the cmdlet again. At run.ps1:3 char:24 + $before = @(get-process <<<< excel | %{$_.Id} ) + CategoryInfo : ObjectNotFound: (excel:String) [Get-Process], ProcessCommandException + FullyQualifiedErrorId : NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.GetProcessCommand

Line 3-5 of my code is as follows:

$before = @(get-process excel | %{$_.Id} ) 
$excel=new-object -com excel.application
$excelId = get-process excel | %{$_.Id} | ?{$before -notcontains $_}

回答1:


I would do it like so:

$before = Get-Process | % { $_.Id }
$excel=new-object -com excel.application
$excelId = Get-Process excel | % { $_.Id } | ? { $before -notcontains $_ }

This sidesteps the issue by just collecting IDs for all processes beforehand. This way, we follow the same steps whether or not there are existing Excel processes. If there are none, then the filter ? { $before -notcontains $_ } will not match and exclude any processes, and we'll just get back the PID for our new process.

However, I don't disagree with Efran Cobisi's answer: it's another valid way to handle this particular scenario.




回答2:


You can use the -ErrorAction parameter, telling PowerShell to SilentlyContinue your script whatever the result of Get-Process is; after that, $? will be set according to the eventual presence of an error (and thus the absence of the process you are looking for too):

$excel = Get-Process excel -ErrorAction SilentlyContinue
if (-not $?) { 'Excel is not running.' }



回答3:


There are already good answers to this question, but I have another approach based on a quirk of Get-Process (and several other cmdlets):

Get-Process 'exce[l]'

Will do exactly what you asked.

The Quirk Explained

The Get-Process cmdlet and many other cmdlets handle names with wildcards differently. While:

Get-Process 'NonExistentProc'

Will generate an error, using:

Get-Process 'NonExistentProc*'

With the wildcard '*' added to the end, will not generate an error. You might guess that an empty array is returned in this case. That makes the most sense to me, but when no matches are found, $null is returned.

For your case if you use:

get-process 'excel*'

You won't get an error if excel isn't running, but you will match 'excelHelper' and any process name that starts with 'excel' which might be a problem. You can work around this by using a very narrow wildcard pattern:

get-process 'exce[l]'

The brackets wildcard will match any one of the characters in the brackets. Usually a set or range is given but here we use the degenerate case of a single character. Only 'excel' will be matched, but because a wildcard is used, not finding a match will not generate an error.

If you want to generalize this to work with any given process name, you can calculate the pattern:

$procName = 'excel'
Get-Process "$($procName.Substring(0, $procName.length-1))[$($procName[-1])]"

(Okay, so that's pretty ugly, but it works and you can break it up if you wish.)

Some alternatives.

@Efran's:

$excel = Get-Process excel -ErrorAction SilentlyContinue

Is good (I upvoted it), but an error is added to $error. In some larger systems I've seen scripts where $error is checked or processed later so I usually prefer to use -ErrorAction Ignore:

$excel = Get-Process excel -ErrorAction Ignore

The documentation says:

Unlike SilentlyContinue, Ignore does not add the error message to the $Error automatic variable.

But also keep in mind:

The Ignore value is introduced in Windows PowerShell 3.0.

But that's becoming less and less of an issue.

@Peter's answer is simple and logically correct (ignoring a possible race condition, see below). I upvoted it. And while there is no real performance problem in this case (I just tested and with 200 processes running on my machine, his code still runs instantly), it bugs me that I'm creating 199 unneeded process objects.

The real potential problem is using the larger set of PIDs (PIDs for all the processes that I'm allowed to see) there's a larger chance for a collision where one of the captured PIDs is for a process that terminates and that PID is reused when the target process is started. In that unlikely (because the time window is so small) but possible event, the target process will be eliminated from consideration (because it's PID will be in the $before list) and it will look like no process was started.

It's a pretty small window, and probably not worrying about, but you could track process StartTime property along with ID to make the chance of a collision vanishingly small. You can eliminate the possibility of PID reuse using [System.Diagnostics.Process]::GetProcessById to open the process (returns a Process object that contains a system handle), but it's probably not worth it (but if you do, be sure to close the handle by calling the Process object's Close method).



来源:https://stackoverflow.com/questions/19616946/powershell-add-catch-to-pick-up-if-there-are-no-excel-processes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!