Chcę napisać skrypt z parametrami, w którym wartości domyślne tych parametrów są przechowywane w pliku json. Niestety, Powershell nie rozpoznaje param(), jeśli nie jest to pierwsza linia kodu w skrypcie.

Ale oczywiście, jeśli jest to pierwsza linia skryptu, nie może załadować konfiguracji z wyprzedzeniem i użyć jej wartości jako wartości domyślnej. Więc jestem w kostce.

config.json:

{"defaultValue":"Default"}

Skrypt Powershell:

$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

Jestem nowy w powershell, czy istnieje sposób na ustawienie domyślnych wartości parametrów później w skrypcie? Lub alternatywnie wczytać parametry później do skryptu?

2
Sandwichnick 18 czerwiec 2021, 13:42

4 odpowiedzi

Najlepsza odpowiedź

Domyślne wartości parametrów można przypisywać za pomocą dowolnych poleceń i o ile wartości domyślne są specyficzne dla parametrów i nie opierają się na wartościach innych parametrów (lub zmiennych zdefiniowanych później w skrypcie), użycie samodzielnych poleceń do określenia wartości domyślne to realna opcja:

param(
  $myparam = (ConvertFrom-Json (Get-Content -Raw $PSScriptRoot/config.json)).defaultValue
)

# Output the parameter value for diagnostic purposes.
"[$myparam]"

Jedyny potencjalny problem polega na tym, że jeśli chcesz w ten sposób przypisać wiele domyślnych wartości parametrów, będziesz musiał duplikować (odmiany) to polecenie:

2
mklement0 18 czerwiec 2021, 18:33

Polecam użycie PSDefaultParameterValues; więc możesz załadować plik JSON do tej tablicy mieszającej na początku skryptu.

Aby obejść param konieczność znajdowania się na początku skryptu i podać nazwę funkcji do użycia w tablicy haszującej PSDefaultParameterValues, możesz zdefiniować podstawową logikę skryptu w definicji funkcji wewnątrz scenariusz. Następnie sam skrypt robi tylko 3 rzeczy:

  • przeczytaj plik JSON i ustaw PSDefaultParameterValues
  • definiuje funkcję
  • wywołuje funkcję

Myślę, że to podejście jest prostsze niż DynamicParams i zachęca do dobrej praktyki enkapsulacji kodu w funkcje wielokrotnego użytku, a skrypty są tak naprawdę tylko wrapperami, które wywołują wspomniane funkcje.

2
jbsmith 18 czerwiec 2021, 15:54

Załóżmy, że Twój config.json wygląda tak:

{
    "Name": "Earthling",
    "Color": "Pink"
}

W zależności od aktualnego scenariusza możesz przekazać ścieżkę do config.json jako parametr i uczynić wszystkie inne parametry opcjonalnymi.

W skrypcie ładujesz konfigurację i podajesz wartości domyślne dla wszystkich parametrów, które nie zostały ustawione w wywołaniu skryptu:

[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"

Skrypt mógłbyś nazwać tak:

PS> ./script.ps1  -ConfigPath ./config.json -Color Blue

Earthling your favourite color is Blue

Ma to tę wadę, że nie można deklarować parametrów jako obowiązkowych. Alternatywnym podejściem, które umożliwia zadeklarowanie parametrów jako obowiązkowych, byłoby potokowanie konfiguracji do skryptu:

[CmdletBinding()]
param (
    [Parameter(ValueFromPipelineByPropertyName,Mandatory)]
    [string]
    $Name,

    [Parameter(ValueFromPipelineByPropertyName,Mandatory)]
    [string]
    $Color
)

    Write-Host "$Name your favourite color is $Color"

Inwokacja byłaby wtedy:

PS> Get-Content ./params.json | ConvertFrom-Json | ./script.ps1 -Color Red

Earthling your favourite color is Red
3
Manuel Batsching 18 czerwiec 2021, 14:16

W tym celu możesz użyć bloku DynamicParam { } i prawdopodobnie polecenia cmdlet Set-Variable.
Wadą jest to, że nie można ustawić powiązanej zmiennej (takiej jak $Name), ale można zamiast tego użyć $PSBoundParameters[$Name] lub przypisać nazwy samych zmiennych w bloku Begin.
Szybki i brudny przykład (nie sprawdzaj np. czy powiązany parametr rzeczywiście istnieje):

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
1
iRon 18 czerwiec 2021, 15:53