PowerShell : use parameter validation without thro

2020-07-18 05:11发布

I'd like to call a PowerShell script this way :
script.ps1 -path mypath\to\files\ -days 6 -hours 0

To validate the command line arguments, I can either do it by hand, either rely on the param syntax :

Param (
    [Parameter(Mandatory=$true )] [string] $path,
    [Parameter(Mandatory=$false)] [int]    $days,
    [Parameter(Mandatory=$false)] [int]    $hours
)

 

 

If I use the param syntax :

  • the param definition must be the first line in the script (excluding comments). Okay, not a problem for me

  • in case of incorrect parameters, I can't catch the error (for example to display a custom error message)

     

I'd like to display a custom error message when the script is called with wrong parameters.
Is it possible, and how, to catch the exception in case of parameter error ?

5条回答
做自己的国王
2楼-- · 2020-07-18 05:18

The examples are for functions (simple and advanced) but the same idea should work for scripts with param as well:

# Simple function.
# Everything not declared in `param` goes to $args.
# If $args is not empty then there are "invalid" parameters or "unexpected" arguments
function test {
    param (
        [string]$path,
        [int]$days,
        [int]$hours
    )
    # check $args and throw an error (in here we just write a warning)
    if ($args) { Write-Warning "Unknown arguments: $args" }
}

Or

# Advanced function.
# For an advanced function we can use an extra argument $args
# with an attribute `[Parameter(ValueFromRemainingArguments=$true)]`
function test {
    param (
        [Parameter(Mandatory=$true )] [string] $path,
        [Parameter(Mandatory=$false)] [int]    $days,
        [Parameter(Mandatory=$false)] [int]    $hours,
        [Parameter(ValueFromRemainingArguments=$true)] $args
    )
    # check $args and throw an error (in this test we just write a warning)
    if ($args) { Write-Warning "Unknown arguments: $args" }
}

The following test:

# invalid parameter
test -path p -invalid -days 5

# too many arguments
test -path p 5 5 extra

in both cases produces the same output:

WARNING: Unknown arguments: -invalid
WARNING: Unknown arguments: extra
查看更多
老娘就宠你
3楼-- · 2020-07-18 05:20

you can do that with the param syntax, if you add a string-type dummy-parameter with the property "ValueFromRemainingArguments". Then you can check this dummy-parameter in your script and take appropriate actions, e.g.:

Param(
    [Parameter(Mandatory=$false)]
    [SWITCH]$myparam1,

    [Parameter(Mandatory=$false)]
    [SWITCH]$myparam2,

    [parameter(Mandatory=$false,ValueFromRemainingArguments=$true)]
    [STRING]$dummy     
    )

if ($dummy -eq anythingYouDontLike) throwAMessageOrSomething.
查看更多
乱世女痞
4楼-- · 2020-07-18 05:26

So okay, it is not possible to use param AND to catch the related exceptions.

查看更多
Evening l夕情丶
5楼-- · 2020-07-18 05:36

In the Begin block you can always do further validation on the parameters but if the parameter is wrong, I think you wouldn't want to continue execution. That is, you'd want to throw a terminating error. Here's an example:

param (
    [Parameter(Mandatory=$true )] [string] $path,
    [Parameter(Mandatory=$false)] [int]    $days,
    [Parameter(Mandatory=$false)] [int]    $hours
)

Begin {
    if ($hours -lt 0 -or $hours -gt 23) {
        throw "Hours parameter must be between 0 and 23"
    }
}

That said, I'm not sure that's any better than using PowerShell's built-in parameter validation functionality e.g.:

param (
    [Parameter(Mandatory=$true )] [string] $path,
    [Parameter(Mandatory=$false)] [int]    $days,
    [Parameter(Mandatory=$false)]
    [ValidateRange(0,23)]
    [int]
    $hours
)
查看更多
祖国的老花朵
6楼-- · 2020-07-18 05:38

A possible workaround is to wrap your actual function in another one. Something similar to a private/public relation. Example:

function Example-Private
{
    [CmdletBinding()]
    Param
    (
        [ValidateNotNullOrEmpty()]
        [string]$Arg1,
        [ValidateNotNullOrEmpty()]
        [string]$Arg2   
   )

   # Do what you need
}

function Example-Public
{
    [CmdletBinding()]
    Param
    (
        [string]$Arg1,
        [string]$Arg2   
    )

    try
    {
       Example-Private $Arg1 $Arg2
    }
    catch
    {
       # Display a user-friendly message, save exception into a log file, etc.
    }
}

If you are working on a module you could take a look here how to export your public functions and hide the private ones: Export Powershell Functions

查看更多
登录 后发表回答