Author Posts

March 5, 2014 at 7:19 am

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.

March 5, 2014 at 7:23 am

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).

March 5, 2014 at 7:25 am

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 }

March 5, 2014 at 7:36 am

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?

March 5, 2014 at 7:40 am

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 $_.

March 5, 2014 at 7:48 am

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!