Comparison operator as parameter on a function

Hello All,

I am writing a basic function that should show the output older than or newer than X days

If I would like to parse as parameter Newer or Older, and in the end, it the same query just switching the operator from -lt or -gt.
Do I need to write two lines of code or would be a way to parameterized the comparison operator?

$vms | ? { $_.created -lt (get-date).AddDays(-$($daysback))} for something like

if ($OperatorParam -eq “newer”) { $OperatorParam = “-gt” } else { $OperatorParam = “-lt” }
$vms | ? { $_.created ($($OperatorParam)) (get-date).AddDays(-$($daysback))}

 

Thanks for your help.

I hope I understand the question correct,

Regardless of the approach you go for, make it simpler in a way you yourself or other can understand what you are doing.

Adding a lot of parameters to your code might make things more complex to read.

for me, I can find it way much easier to read the first line than the second line.

One more thing, Why do you need to parametrize the operators ?! what is the purpose

 

When you store a group of characters like -lt in a variable, PowerShell interprets it as a string and stores it as that type (see Type of Variables). When you expand that variable, it is expanded as a string, so when you try to include it as part of a command it will be interpreted as a string and not as an operator. For instance, if you wanted to do something like this:

Get-ChildItem | Where-Object -Property LastWriteItem -lt (Get-Date).AddDays(-30)

it won’t work like this:

$days = 30
$operator = '-lt'

Get-ChildItem | Where-Object -Property LastWriteTime $operator (Get-Date).AddDays(-$days)

However, you can use Invoke-Expression to execute a string stored in a variable as a command. So, what you can do is assemble your command as a string with the contents of your other variables and then execute the entire command with Invoke-Expression, like this:

[int]$days = 30
[string]$operator = '-lt'

[string]$command = "Get-ChildItem | Where-Object -Property LastWriteTime $operator (Get-Date).AddDays(-$days)"

Invoke-Expression $command

Warning
Be sure to read the Caution message on the Invoke-Expression documentation page. You should validate the parameter input that you accept for this function to prevent a user from inserting arbitrary code.

For this function, you should cast $daysback to the integer type so that it will only accept numbers as inputs, and for the newer/older option you could simply use a switch parameter. That would look something like this:

Param(
    [Parameter(Mandatory=$false)][int]$daysback=0,
    [Parameter(Mandatory=$false)][switch]$Newer
) #Param

if ( $Newer ) { $OperatorParam = '-gt'}
else { $OperatorParam = '-lt' }

With this setup, if you include the parameter -Newer when you call the function then it will use -gt, and if you do not include -Newer then it will default to -lt.

Thanks for your replies.

Yes, I just wanted to avoid typing two times the same command one for -lt and the same for -gt, and I was wondering if there would be an efficient way to do it.
Thanks, @grokkit for the detailed explanations and remarks. So even if in the end by using invoke-command is possible to achieve what I was asking for, It doesn’t seem to help in being a more efficient solution, so I will go for the approach of using “if” and typing the command twice.

Greets!

 

 

Well, don’t give up so quickly!

If we do it like this:

function Filter-ByOperator {
    [CmdletBinding()]Param(
        [Parameter(Mandatory=$false)][int]$Days=0,
        [Parameter(Mandatory=$false)][switch]$Newer
    ) #Param

    if ( $Newer ) { $Operator = '-gt' }
    else { $Operator = '-lt' }

    Invoke-Expression "Get-ChildItem | Where-Object -Property LastWriteTime $Operator (Get-Date).AddDays(-$Days)"

} #function

we can eliminate the step of assigning the string to the $command variable and just execute it directly. However, we still need the if statement, so it doesn’t seem particularly more efficient than just writing the command out twice like this:

function Filter-ByOperator {
    [CmdletBinding()]Param(
        [Parameter(Mandatory=$false)][int]$Days=0,
        [Parameter(Mandatory=$false)][switch]$Newer
    ) #Param

    if ( $Newer ) { Get-ChildItem | Where-Object -Property LastWriteTime -gt (Get-Date).AddDays(-$Days) }
    else { Get-ChildItem | Where-Object -Property LastWriteTime -lt (Get-Date).AddDays(-$Days) }

} #function

But, if we change the parameters a bit, we can eliminate the need for the if statement entirely:

function Filter-ByOperator {
    [CmdletBinding()]Param(
        [Parameter(Mandatory=$false)][int]$Days=0
        [Parameter(Mandatory=$false)][ValidateSet("lt","gt","le","ge","eq")][string]$Operator="lt"
    ) #Param

    Invoke-Expression "Get-ChildItem | Where-Object -Property LastWriteTime -$Operator (Get-Date).AddDays(-$Days)"

} #function

This should be more efficient, though it does require the user to specify an operator. The inputs for $Operator are limited by ValidateSet to prevent arbitrary strings from being executed; if not specified, it will default to lt.

This also makes the function more flexible, as you could get output for a specific day with eq.