Search code examples
functionpowershellparametersveeam

Cannot use variable in a Where-Object in a function


I am trying to have a function that can count jobs based on the LatestStatus value that I would pass a parameter. So far what I got:

Function JobCountStatus {
  Write-Output (Get-VBRJob | ?{$_.Info.LatestStatus -eq $args} | Measure-Object).Count
}

The issue is that as I've read somewhere there will be a subshell(?) executing the where so the argument is not passed.

If I replace the $args with a specific string like "Failed" it will work.

Is there a way to overcome this? I do not want to write separate functions for all possible values.

I would appreciate any comments - Thanks


Solution

  • $args is an array, not a single value. In any case, a ScriptBlock {} is an unnamed function and $args has it's own meaning within it, so you can't use it without some modification in something like Where-Object. You would have to store the elements of $args as another variable or multiple variables to reference within a child ScriptBlock. Not a huge change for this function, but for one where more parameters are expected this can result in a lot of unnecessary code which can be difficult to maintain.


    I prefer to recommend defining named parameters in most cases, and this would be a simpler change than making the parent $args work in a child ScriptBlock:

    Function JobCountStatus {
      Param(
        [string]$Status
      )
      ( Get-VBRJob | Where-Object { $_.Info.LatestStatus -eq $Status } ).Count
    }
    

    I've also made a few more changes in this function, I'll explain below:

    • Use Param() to strongly define parameters by name. You could use the simpler syntax of function JobCountStatus([string]$Status) {} but for this case it's really a matter of preference for which technique to use. Using Param() is something I recommend as a matter of convention, but you'll need to use either technique to get a named parameter.
    • I replaced the $args reference with $Status.
    • Your use of Measure-Object is extraneous and so I've removed it. Where-Object returns a collection which already has the Count property.
    • You can use ? if you want but it's considered best practice to omit aliases and use full cmdlet names in scripts and modules, so I've replaced ? with Where-Object.

    Note that you can invoke the function/cmdlet (the difference is minimal for PowerShell-defined cmdlets) with or without the parameter, as when you don't define a positional order, the order is automatically determined in the order of declaration:

    # Either will work
    JobCountStatus -Status Running
    JobCountStatus Running
    

    Here is some more documentation you may find useful:

    @Ash's answer gives some more advanced examples of what you can do with param() which are mentioned in the the Advanced Functions link above. You cannot use advanced parameter attributes with the simple function syntax I mentioned in the first bullet point.