3

I'm new to powershell and have only used script parameters of type string yet. Now I try to create a script which deletes backups which are older than a given time span, similar to this one:

Param(
    [parameter(Mandatory=$true)]
    [string] $BackupFolder,
    [parameter(Mandatory=$true)]
    [Timespan] $StorageDuration
)
$BackupFolder | ls -ad |? { (New-TimeSpan -Start $_.CreationTime -End Get-Date) -ge $StorageDuration} |% {$_|rm -Confirm:$false -Recurse}

Is there a way for a caller from the PS command line to pass a time span argument like e.g. 'New-TimeSpan -Days 7' ? I made a workaround where only the days are passed as an argument but I don't like this limitation and would prefer to pass the whole time span as a single parameter, possibly including days, minutes, hours and so on.

There are also two minor issues in my script which I don't understand. Maybe you can also answer these to help me improving my powershell knowledge:

  1. MSDN says that d and ad are aliases for the Directory parameter. But when I use d instead of ad, I receive an error message saying that no argument for the parameter depth was specified. Did they mix something up there?

  2. Why do I have to write a colon between -Confirm and $false? When I omit that, it tells me that no positional parameter takes the value false.

1 Answers1

5
  • To pass a [timespan] value as an argument - or, indeed, any expression - enclose it in (...).
    See this answer for how PowerShell parses command arguments.

    # Pass a [timespan] to cmdlet Get-Foo
    Get-Foo (New-TimeSpan -Days 7)
    
  • -d not being accepted as an alias of -Directory is a bug:

    • -d is mistakenly interpreted as the prefix form of the -Depth parameter, even though -d is explicitly declared as an alias of -Directory (as is -ad).
    • The proper behavior is for parameter aliases to always be accepted as-is - even if their name happens to be a prefix of a different parameter (such as -Depth in this case).

      • As an aside: If the -d alias didn't exist, PowerShell would normally sensibly report a parameter name 'd' is ambiguous error, because the name prefix specified is not long enough to uniquely identify the target parameter.[1]
    • See this GitHub issue.

  • -Confirm is a switch parameter, which normally doesn't take an argument: (with exceptions[2]) it is a switch's absence or presence that determines its value.

    • Because switch parameters don't take an argument, whatever token follows them is considered the next argument.
    • Therefore, you must use : after the switch name to tell PowerShell that the following token is the switch's argument.
      • As an aside: the : syntax works even with non-switch parameters, i.e. those that always require an argument, but it is rarely used in that case.

[1] PowerShell generally allows you to use only the prefix of a parameter name as long as that prefix is unique among all parameters supported by a given command. The ability to take such shortcuts is what PowerShell calls elastic syntax, and it is intended for interactive use (on the command line). You should avoid elastic syntax in scripts, because such scripts may break in the future, if a command introduces a new parameter that makes a previously unique name prefix no longer unique.

[2] Your use case, -Confirm:$false shows one such exception: it signals the explicit intent not to ask for confirmation, which is not the same as omitting the -Confirm switch: the former unconditionally opts out of prompting, whereas the latter makes the behavior dependent on the target command's declared impact level and the current value of the $ConfirmPreference preference variable.

mklement0
  • 382,024
  • 64
  • 607
  • 775