Reputation: 1466
I want to write a script with parameters, where the default values for those parameters are stored in a json file. Unfortunately, Powershell does not recognise param()
if it is not the first line of code within the script.
But of course if it is the first line of the script, it cannot load the config in advance and use its value as default value. So I am in anpickle.
The config.json
:
{"defaultValue":"Default"}
The powershell script:
$configPath = "config.json"
$configPath = Join-Path $PSScriptRoot $configPath
# read config
$config = Get-Content $configPath | ConvertFrom-Json
write-host $config.defaultValue
param($myparam = $config.defaultValue)
write-host $myparam
I am new to powershell, is there a way to set default values of parameters later in the script? Or alternatively, read parameters later into the script?
Upvotes: 3
Views: 2487
Reputation: 437100
Parameter default values can be assigned via arbitrary commands, and as long as your default values are parameter-specific and do not rely on the values of other parameters (or variables defined later in the script), the use of self-contained commands to determine default values is a viable option:
param(
$myparam = (ConvertFrom-Json (Get-Content -Raw $PSScriptRoot/config.json)).defaultValue
)
# Output the parameter value for diagnostic purposes.
"[$myparam]"
The only potential problem is that if you want to assign multiple parameters' default values this way, you'll have to duplicate (variations of) this command:
Performance-wise, I suspect this won't be a problem, though (unless the JSON is file is large and there are many parameters).
If you dislike the duplication aspect for maintenance reasons, use an after-the-fact approach based on the automatic $PSBoundParameters
variable, as shown in Manual Batsching's helpful answer.
Upvotes: 2
Reputation: 1666
I would recommend using PSDefaultParameterValues; so you could load the JSON file into this hashtable at the start of your script.
To get around param
needing to be at the start of the script, and to give you a function name to use in the PSDefaultParameterValues
hashtable, you can define the core logic of your script in a function definition inside the script. Then the script itself just does 3 things:
I think this approach is simpler than DynamicParams and encourages the good practive of encapsulating your code into re-usable functions, and scripts are really just wrappers that invoke said functions.
Upvotes: 3
Reputation: 23623
You might want to use the DynamicParam { }
block and possibly the Set-Variable
cmdlet for this.
The disadvantage is that you can't set the related variable (like $Name
) but could use $PSBoundParameters[$Name]
instead or assign the bare variable names within the Begin
block.
Quick and dirty example (no check e.g. if the related parameter really exist):
Function Test {
[CmdletBinding()] param (
[Parameter()][string]$Name,
[Parameter()][string]$Color
)
DynamicParam {
$Defaults = ConvertFrom-Json -AsHashTable '{
"Name": "Earthling",
"Color": "Pink"
}'
ForEach ($Key in $Defaults.get_Keys()) {
if (!$PSBoundParameters.ContainsKey($Key)) { $PSBoundParameters[$Key] = $Defaults[$Key] }
}
}
Begin {
ForEach ($Key in $PSBoundParameters.get_Keys()) { Set-Variable $Key $PSBoundParameters[$Key] }
}
Process {
Write-Host 'Name:' $Name
Write-Host 'Color:' $Color
}
}
Test -Name Jim
Name: Jim
Color: Pink
Upvotes: 2
Reputation: 3596
Lets assume your config.json
looks like this:
{
"Name": "Earthling",
"Color": "Pink"
}
Depending on your actual scenario you could pass the path to the config.json
in as a parameter and make all the other parameters optional.
Within the script you load the config and provide default values for all parameters that have not been set in the script invocation:
[CmdletBinding()]
param (
[Parameter()]
[string]
$Name,
[Parameter()]
[string]
$Color,
[Parameter()]
[string]
$ConfigPath
)
if ($ConfigPath -in $PSBoundParameters.Keys) {
$config = Get-Content $ConfigPath | ConvertFrom-Json
if ('Name' -notin $PSBoundParameters.Keys) {
$Name = $config.Name
}
if ('Color' -notin $PSBoundParameters.Keys) {
$Color = $config.Color
}
}
Write-Host "$Name your favourite color is $Color"
You would call the script like this:
PS> ./script.ps1 -ConfigPath ./config.json -Color Blue
Earthling your favourite color is Blue
That has the downside, that you cannot declare parameters as mandatory. An alternative approach that allows declaring parameters as mandatory would be to pipe the configuration into your script:
[CmdletBinding()]
param (
[Parameter(ValueFromPipelineByPropertyName,Mandatory)]
[string]
$Name,
[Parameter(ValueFromPipelineByPropertyName,Mandatory)]
[string]
$Color
)
Write-Host "$Name your favourite color is $Color"
The invocation would then be:
PS> Get-Content ./params.json | ConvertFrom-Json | ./script.ps1 -Color Red
Earthling your favourite color is Red
Upvotes: 3