Fabricating a custom -Filter in my Advanced Function

This topic contains 5 replies, has 3 voices, and was last updated by Profile photo of Micah Battin Micah Battin 3 years, 1 month ago.

  • Author
    Posts
  • #13575
    Profile photo of Micah Battin
    Micah Battin
    Participant

    I have an Advanced Function that is designed to stream packets to me and they come into my NIC, similar to Wireshark. The problem is that I want to implement a -Filter parameter in my function rather than piping the whole function to Where-Object.

    At first glance, I thought I would get away with setting a a $filter paramater as a scriptblock and where I am streaming the object in one at a time, piping THAT object to Where-Object like this:
    $Packet Where-Object -FilterScript $Filter

    This however didn't work as I had planned. I am going to spare the long explanation of trial and error for the time being. Does anyone know of resources out there for implementing my own -Filter Parameter? I have searched for the last hour and a half and had no luck.

  • #13580
    Profile photo of Micah Battin
    Micah Battin
    Participant

    Never Mind. I tried an additional test and it succeeded. So the Fundamental question that I was asking has been solved. This is the test that I ran and succeeded with:


    Function Test {
    [CmdletBinding()]
    param
    (
    [scriptblock]$Filter
    )

    Begin {
    }
    Process {

    $Packet = New-Object PSObject -Property @{
    Name = "MyStuff"
    Address = "1.1.1.1"
    Port = 135
    Notes = "This is a test."
    }

    If ($Filter) {
    $Packet | Select-Object Name,Address,Port,Notes | Where-Object $Filter
    }
    Else {
    $Packet | Select-Object Name,Address,Port,Notes
    }
    }
    End {
    }
    }

    Test -Filter {$_.Name -like "Stuff"}
    # No Results
    Test -Filter {$_.Name -like "*Stuff*"}
    # Results

    I lied... There is a followup question that comes to mind. With the -filter in Get-ADUser, I can specify
    {SamAccountName -eq "MyUserName"}
    Rather than
    {$_.SamAccountName -eq "MyUserName"}

    Does anyone have an idea on how I can mimic that functionality in the test code above?

  • #13581
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    Get-ADUser's filter is much more complex. It accepts a string, not a ScriptBlock, and it has code to parse that string and turn it into an LDAP filter behind the scenes. Writing your own code to parse a complex expression and evaulate it against an input object would be a lot of work, with the only real benefit being that the user can save three characters of typing by eliminating $_.

  • #13583
    Profile photo of Micah Battin
    Micah Battin
    Participant

    I understand. I guess the bigger concern of mine is that trying to remember several different -filter syntax's for different commands might be a little too much with their already high stress from a heavy workload. I am just trying to match it to AD because it is what they are used to. Now that I know there is no "easy" way to do it, I will likley go on a glorious adventure developing a code snippet to handle it in the way that I desire.

    Thanks for all your help!

  • #13576
    Profile photo of Don Jones
    Don Jones
    Keymaster

    So, I am completely not understanding what you're asking. I think you probably need to share some code snippets of what you're doing.

    As far as implementing your own -Filter parameter... that's easy. Doing something with it depends entirely on what your code is doing. I mean, there's no built-in "here's how to write a filter." For example,

    
    function foo {
      param([scriptblock]$filter)
      Get-Something | Where-Object -Filter $filter
    }
    

    Essentially works, but I've no idea if that's meaningful in what you're trying to do. And I should point out that this is not functionally all that different from piping the whole function to Where-Object, if your function is normally outputting one object at a time to the pipeline. A filter is really only meaningful in a function if it can prevent data from entering PowerShell at all, a la the -Filter on Get-WmiObject. Once you're "filtering using PowerShell," it doesn't necessarily matter where you do it (again, all things being equal – not seeing your code it's hard to make definitive statements).

  • #13577
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    I doubt the performance will be any better than using Where-Object, but you can do something like this:

    function YourFunction
    {
        [CmdletBinding()]
        param (
            [Parameter(ValueFromPipeline)]
            $InputObject,
    
            [scriptblock]
            $Filter
        )
    
        process
        {
            if ($null -eq $Filter -or $Filter.Invoke($InputObject))
            {
                # Object passed your filter (or no filter was specified); do something here
                $InputObject
            }
        }
    }
    

    With the code written like that, the two following commands would produce identical results:

    $strings = '1','22','333','4444','55555'
    
    $strings | YourFunction | Where-Object { $_.Length -gt 3 }
    $strings | YourFunction -Filter { $_.Length -gt 3 }
    

You must be logged in to reply to this topic.