PowerShell Where-Object gotcha

When using Where-Object in script block form, e.g Where-Object {$_.LastWriteTime -gt (Get-Date).AddHours(-1)}, it is inefficient because the Get-Date command will be executed for every single pipeline element.

In general it’s recommended to use precomputed values for filtering in script block form:

$OneHourAgo = (Get-Date).AddHours(-1)
Get-ChildItem -File | Where-Object { $_.LastWriteTime -gt $OneHourAgo }

However, in the newer form that doesn’t require the use of $psitem / $_, the command will only run once:

# test function which logs when it gets called
PS C:\> function test { write-verbose -Verbose -Message $(get-date) ; get-date }

# parameter form, it logs being called once
PS C:\> gci | where LastWriteTime -gt (test).addhours(-1)
VERBOSE: 14/01/2020 00:08:52

# scriptblock form, it logs being called many times
PS C:\> gci | where {$_.LastWriteTime -gt (test).addhours(-1)}
VERBOSE: 14/01/2020 00:09:15
VERBOSE: 14/01/2020 00:09:15
VERBOSE: 14/01/2020 00:09:15
[..]