When writing PowerShell scripts that collect information from a data source with many items, it's common to filter out some of those items with the Where-Object cmdlet. The Where-Object cmdlet allows a scripter to provide a scriptblock that, when executed, returns a boolean value that restricts the items sent to the pipeline.

For example, perhaps I'm querying files in a particular folder with the Get-ChildItem cmdlet and want to find all files changed only within the past ten days. One way I could do this is by using Where-Object and the FilterScript parameter.

Get-ChildItem | Where-Object -FilterScript { $_.LastWriteTime.AddDays(10) -gt (Get-date) }

This works well as is, but what if the criteria you're using for FilterScript change depending on input in a script or function? Perhaps this line is part of a script that finds files ten days or older sometimes, but other times you want to find files older than ten days. In this case, the FilterScript scriptblock will change. But instead of duplicating code in each scenario, it's best to change what only needs changing: the value for FilterScript.

To demonstrate, let's wrap this line in a fictional function of some sort that allows the user to query files in a folder either older than or newer than a number of days. If we duplicated this line, it might look something like the code below:

function Get-Files {
    [CmdletBinding(DefaultParameterSetName = 'Default')]
    param(
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$FolderPath,

        [Parameter(ParameterSetName = 'OlderThan')]
        [ValidateNotNullOrEmpty()]
        [switch]$OlderThan,

        [Parameter(ParameterSetName = 'NewerThan')]
        [ValidateNotNullOrEmpty()]
        [switch]$NewerThan,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [int]$Days
    )

    $now = Get-Date
    if ($OlderThan.IsPresent) {
        Get-ChildItem | Where-Object -FilterScript { $_.LastWriteTime.AddDays($Days) -lt $now }
    } elseif ($NewerThan.IsPresent) {
        Get-ChildItem | Where-Object -FilterScript { $_.LastWriteTime.AddDays($Days) -gt $now }
    }
}
Displaying files that have changed within the past 10 days

Displaying files that have changed within the past 10 days

This function does what we intended, but it's not the best. We're duplicating the Get-ChildItem references when we don't have to. Instead, the scriptblock used with the FilterScript parameter needs to change. Since the value of FilterScript is just a scriptblock, we can define it as a variable ahead of time and make that change based on input from the function parameters.

Subscribe to 4sysops newsletter!

function Get-Files {
    [CmdletBinding(DefaultParameterSetName = 'Default')]
    param(
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$FolderPath,

        [Parameter(ParameterSetName = 'OlderThan')]
        [ValidateNotNullOrEmpty()]
        [switch]$OlderThan,

        [Parameter(ParameterSetName = 'NewerThan')]
        [ValidateNotNullOrEmpty()]
        [switch]$NewerThan,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [int]$Days
    )

    $now = Get-Date
    if ($OlderThan.IsPresent) {
        $filterScript = { $_.LastWriteTime.AddDays($Days) -lt $now }
    } elseif ($NewerThan.IsPresent) {
        $filterScript = { $_.LastWriteTime.AddDays($Days) -gt $now }
    }
    Get-ChildItem | Where-Object -FilterScript $filterScript
}

This is just a simple example of how you can use this technique. By moving the value of FilterScript to a variable, you can build these dynamic filter scripts and pass them to whichever command you're filtering in PowerShell!

avatar
3 Comments
  1. Andrey Voronin 4 years ago
    function Get-ModifiedIn {
     param (
     [int]$Days,
     [switch]$Revert
     )
     $now = Get-Date
    
    If ($Revert) { $op = "-lt"} else { $op = "-gt"}
     $sb = [scriptblock]::Create("`$_.LastWriteTime.AddDays($Days) $op '$now'")
     return $sb
    }
    
    
    Get-ChildItem "C:\Windows\Logs" -Recurse | ? { $_.FullName -notmatch "WindowsUpdate"} | ? (Get-ModifiedIn -Days 1)
    avatar
  2. G4rp 4 years ago

    Dear Adam,

    thanks for the good post!

    For learning purpose I would like to improve the function and validate that the two switches cannot be used together.

    Any idea how to do it?

    Regards

    G4rp

    • Panzerbjrn 3 years ago

      Hey there, you'd have to use parameter sets IIRC…

      avatar

Leave a reply

Your email address will not be published.

*

© 4sysops 2006 - 2022

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account