providing a parameter without a value to select a default value: `script -log` vs `script -log filename` [duplicate]
Asked Answered
H

1

1

What I want is a combo of a switch parameter and a string parameter.

Where there are 3 scenarios (not just 2):

  1. -log not given
  2. -log given, default filename
  3. -log filename given

All these states could exist, but I think powershell prevents them.

You can get the first 2 with a [switch] parameter.

You can get 1 and 3 with a [string]$log = 'default'

But I see no way to get all 3.

One use-case is simple and commonplace. I want to either turn a log file on or off, and either let the script use a default logfile or provide my own. Simple. Easy. But I can't figure out how. (I can figure out several workarounds, but no direct way in the language as currently defined.)

One might think that [AllowNull()] would allow -log with no value, but it does not. It only allows -log $null (as called from powershell; called from other contexts, who knows?) (And anyway powershell turns the null into an empty string)

Another way to state this is that while powershell permits Mandatory and non-Mandatory parameters, all parameter values are Mandatory (if the parameter is provided and is not a switch) and there's no way to turn that off.

I believe the following code would work if powershell would not object to the parameters given. But powershell does in fact object (throws an error).

Function  Test-Func {
    Param(
        [string]$log = 'default_logfile.log'
    )
    $do_log = 'uninit'
    if ($PSBoundParameters.ContainsKey('log')) {
        $do_log = $true
        '-log provided' | Tee-Object -Append $log
    }
    else {
        $do_log = $false
        '-log not provided'  # no Tee-Object 
    }
    $msg = "log='$log'"
    $msg 
    if ($do_log) {
        $msg | Out-File -Append $log
    }
}

'1'
Test-Func
'2'
Test-Func -log 'myfile.log'
'3'
Test-Func -log          # Error: Test-Func : Missing an argument for parameter 'log'. 
# I want it to use the default_logfile in this case.

Above code produces the output:

1
-log not provided
log='default_logfile.log'
2
-log provided
log='myfile.log'
3
Test-Func : Missing an argument for parameter 'log'. Specify a parameter of type 'System.String' and try again.
At F:\tech\windows_10\powershell\args_arguments_parameters_example_advanced_no_value_post.ps1:26 char:11
+ Test-Func -log          # Error: Test-Func : Missing an argument for  ...
+           ~~~~
    + CategoryInfo          : InvalidArgument: (:) [Test-Func], ParameterBindingException
    + FullyQualifiedErrorId : MissingArgument,Test-Func

and myfile.log contains:

-log provided
log='myfile.log'

and default_logfile.log is not created.

EDIT:

The duplicate question has a thorough discussion on this. Contemplates a syntax of -log:value using the : char like a switch.

Why?

Because otherwise if parameter values were optional, how would you tell if the next string is a parameter value to -log or a value for the next parameter, as this code shows.

Function  Test-Func {
    Param(
        [string]$log = 'default_logfile.log'
        ,
        [string]$confounding_param
    )
    $do_log = 'uninit'
    if ($PSBoundParameters.ContainsKey('log')) {
        $do_log = $true
        '-log provided' | Tee-Object -Append $log
    }
    else {
        $do_log = $false
        '-log not provided'  # no Tee-Object 
    }
    $msg = "log='$log' confounding_param='$confounding_param'"
    $msg 
    if ($do_log) {
        $msg | Out-File -Append $log
    }
}

'1'
Test-Func
'2'
Test-Func -log 'myfile.log'
'3'
Test-Func -log          # Error: Test-Func : Missing an argument for parameter 'log'. 
# I want it to use the default_logfile in this case.
'4'
Test-Func -log 'confounding param string'  # how can you tell if the next param is a value of -log or the next param
'5'
Test-Func -log -confounding_param 'confounding param string'  # this is how can you tell but then the named param is mandatory in this case which is not ideal

$whoops_logfile = 'confounding param string'
if (Test-Path $whoops_logfile) {
    dir $whoops_logfile
    del -Verbose $whoops_logfile
}

produces the output:

1
-log not provided
log='default_logfile.log' confounding_param=''
2
-log provided
log='myfile.log' confounding_param=''
3
Test-Func : Missing an argument for parameter 'log'. Specify a parameter of type 'System.String' and try again.
At F:\tech\windows_10\powershell\args_arguments_parameters_example_advanced_no_value_post.ps1:28 char:11
+ Test-Func -log          # Error: Test-Func : Missing an argument for  ...
+           ~~~~
    + CategoryInfo          : InvalidArgument: (:) [Test-Func], ParameterBindingException
    + FullyQualifiedErrorId : MissingArgument,Test-Func
 
4
-log provided
log='confounding param string' confounding_param=''
5
Test-Func : Missing an argument for parameter 'log'. Specify a parameter of type 'System.String' and try again.
At F:\tech\windows_10\powershell\args_arguments_parameters_example_advanced_no_value_post.ps1:33 char:11
+ Test-Func -log -confounding_param 'confounding param string'  # this  ...
+           ~~~~
    + CategoryInfo          : InvalidArgument: (:) [Test-Func], ParameterBindingException
    + FullyQualifiedErrorId : MissingArgument,Test-Func



    Directory: C:\Users\techuser199


Mode                 LastWriteTime         Length Name                                                                               
----                 -------------         ------ ----                                                                               
-a----        10/21/2024   9:46 PM            534 confounding param string                                                           
VERBOSE: Performing the operation "Remove File" on target "C:\Users\techuser199\confounding param string".


Heavenly answered 17/10 at 19:21 Comment(0)
D
0

PowerShell assigns defaults to parameters if the parameter is not passed.

PowerShell also only allows omitting the value (argument) for a parameter if it is a switch parameter.

So, you can only use -log if $Log is a switch.

If it's a switch, you get 2 options...either there or not.

Also, PSBoundParameters doesn't contain parameters that aren't passed and are instead set to default values.

You could allow empty strings to be passed and then check for that. Something like this:

if($PSBoundParameters.Contains('Log')){
   if($Log -eq ''){ 
     $Log='default_logfile.log'
   } 
} else {
   #No tee
}
Duester answered 17/10 at 19:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.