Gyakran szükséges hibakeresés közben paraméterek értékeinek vizsgálatára, ami bizonyos esetekben nem is olyan egyszerű feladat, mint amennyire elsőre látszik.
Vegyük a következő példafüggvényt:
function Test-Param {
[cmdletbinding()]
param(
[Parameter(ValueFromPipeline = $true)]
[int]$inputNumber,
[Parameter()]
[int]$szorzó = 2
)
process{
$inputNumber * $szorzó
}
}
Elmentem a függvénydefiníciót egy szkriptként, egy megszakítási pontot helyezek el a process blokkon belül, majd végrehajtom a szkriptet:
109 . ábra Paraméterek vizsgálatának előkészítése ISE-ben
Ezután meghívva a függvényt a futás megáll a kijelölt sornál, de ha a debug prompt mellett lekérdezem a $PSBoundParaméter változót érdekes módon nem kapok semmit:
PS C:\> 1,4 | Test-Param -szorzó 3
Hit Line breakpoint on
'C:\Users\Tibi\OneDrive\PS\test-param.ps1:10'
[DBG]: PS C:\>> $PSBoundParameters
Ennek az lehet az oka, hogy a debug környezetnek saját $PSBoundParameters változója van és mivel ott nem használunk semmilyen paraméter, természetesen üres eredményt kapunk. Szerencsére egy másik módon is elérhetjük az átadott paraméterek lekérdezését, a $PSCmdlet.MyInvocation.BoundParameters változón keresztül:
[DBG]: PS C:\>> $PSCmdlet.MyInvocation.BoundParameters
Key Value
--- -----
szorzó 3
inputNumber 1
Itt már láthatjuk az $inputNumber aktuális értékét és a $szorzó megadott értékét is. De mi van akkor, ha nem adunk meg a $szorzó-nak explicit értéket, hanem az alaphelyzet szerinti értéket szeretnénk használni:
PS C:\> 1,4 | Test-Param
Hit Line breakpoint on
'C:\Users\Tibi\OneDrive\PS\test-param.ps1:10'
[DBG]: PS C:\>> $PSCmdlet.MyInvocation.BoundParameters
Key Value
--- -----
inputNumber 1
Sajnos ilyenkor a BoundParameters nem mutat semmit. Lehetnek olyan helyzetek, amikor jó lenne ilyenkor az alaphelyzet szerinti érékeket is, például ha naplózni szeretnénk az értékek használatát, hogy később is lássuk, hogy ki hogyan milyen paraméterértékekkel használta a függvényt. Ehhez első lépésként a begin blokk egyszerű bővítésével a kívánt eredményhez juthatunk:
function Test-Param {
[cmdletbinding()]
param(
[Parameter(ValueFromPipeline = $true)]
[int]$inputNumber,
[Parameter()]
[int]$szorzó = 2
)
begin {
if(!$PSBoundParameters.ContainsKey("szorzó") -and $szorzó){
$PSBoundParameters.szorzó = $szorzó
}
}
process{
$inputNumber * $szorzó
}
}
Itt az történik, hogy ha a paraméterek között nem szerepel a szorzó, de ennek ellenére van neki értéke, akkor kibővítem a $PSBoundParameters hashtáblát ezzel a hiányzó információval. Ha újból futtatjuk a függvényt, a megszakítási pontnál már megkapjuk az alaphelyzetben használt paramétert és annak értékét is:
PS C:\> Test-Param -inputNumber 5
Hit Line breakpoint on
'C:\PowerShell\testparamszkript.ps1:15'
[DBG]: PS C:\>> $PSCmdlet.MyInvocation.BoundParameters
Key Value
--- -----
inputNumber 5
szorzó 2
Ez jó megoldás, de a függvényünk karbantartását megnehezíti, mert ha például átnevezzük a $szorzó paraméterünket $multiplier-re, akkor nem szabad elfeledkezni arról, hogy az előbb betett begin blokkbeli hivatkozásokat is módosítsuk, márpedig ott négyszer is szerepel a paraméter neve. Nem beszélve arról sem, ha sok olyan paraméterünk van, amelyeknek alaphelyzet szerinti értéke van. Erre lehet megoldás az alábbi dinamikus módszer, amellyel a függvény a saját forráskódját elemzi és kideríti, hogy melyek a dinamikus paraméterek. Ezt most itt nem akarom részletesen megmagyarázni, majd a 2.32.3 Absztrakt szintaxis fa - AST fejezetben lesz szó a használt AST technológiáról.
function Test-Param {
[cmdletbinding()]
param(
[Parameter(ValueFromPipeline = $true)]
[int]$inputNumber,
[Parameter()]
[int]$szorzó = 2,
[Parameter()]
$mikor = (Get-date)
)
begin {
$ast = (Get-Command $MyInvocation.MyCommand).ScriptBlock.Ast
$varswithdefaultvalue = $ast.FindAll({$true}, $false)[0].body.findall({ $args[0] -is [System.Management.Automation.Language.ParameterAst] }, $false) |
Where-Object { $_.DefaultValue } |
ForEach-Object {$_.name.variablepath.userpath}
if($varswithdefaultvalue){
foreach($var in $varswithdefaultvalue){
if(!$PSBoundParameters.ContainsKey($var)){
$PSBoundParameters.$var = Get-Variable -Name $var -ValueOnly
}
}
}
}
process{
$inputNumber * $szorzó
}
}
Ha az eredeti függvényhez képest berakok egy új paramétert alaphelyzet szerinti értékkel, akkor a begin blokk változtatása nélkül automatikusan kiegészül a BoundParameters lista ezzel:
PS C:\> Test-Param -inputNumber 5
Hit Line breakpoint on
'C:\PowerShell\testparamszkript.ps1:25'
[DBG]: PS C:\>> $PSCmdlet.MyInvocation.BoundParameters
Key Value
--- -----
inputNumber 5
szorzó 2
mikor 2016.02.28. 13:41:17
Még jobb megoldás talán a Get-Command segítségével való paraméterfelderítés:
function ParamsDefaultÉrtékekkel {
param(
[parameter(ParameterSetName = "egy")]
$egy,
[parameter(ParameterSetName = "egy")]
$kettő = 2,
[parameter(ParameterSetName = "egy")]
$üres,
[parameter(ParameterSetName = "másik")]
$három
)
$defaults = @{}
(get-command $MyInvocation.MyCommand.Name).Parameters.GetEnumerator() |
Where-Object {!$PSBoundParameters.ContainsKey($_.key) -and (Get-Variable -Name $_.key -Scope Local -ErrorAction Ignore)} |
ForEach-Object {$defaults.($_.key) = Get-Variable -Name $_.key -ValueOnly -Scope local}
Write-Host "Paraméterek megadott értékekkel:"
$PSBoundParameters | Out-Default
Write-Host "Paraméterek default értékekkel:"
$defaults | Out-Default
}
Ha ezt meghívom egy explicit paraméterrel, akkor ezt kapom:
PS C:\> ParamsDefaultÉrtékekkel -egy 1
Paraméterek megadott értékekkel:
Key Value
--- -----
egy 1
Paraméterek default értékekkel:
Name Value
---- -----
üres
kettő 2
három 3
A $PSBoundParameters változóban ott van az explicit paraméterem, a $defaults-ba pedig én raktam bele azokat, amelyek léteznek, mint változók a függvény érvényességi területén a függvény objektumának paramér tulajdonsága alapján.
Itt egy érdekes dologra hívnám fel a figyelmet. Ugyan én az „egy” nevű paraméterkészletet használtam, ennek ellenére a $három paraméterem is megkapta az alaphelyzet szerinti értéket! Ez számomra meglepő volt, mert én azt feltételeztem, hogy ilyenkor a „másik” paraméterkészlet elemeivel nem is foglalkozik a PowerShell, de ez nincsen így.
Megjegyzés:
Ráadásul ez PowerShell 2.0-ban másképp volt, ha ugyanezt PS 2.0-ban futtatjuk, akkor a $három nem fog szerepelni a paraméterek között:
PS C:\WINDOWS\system32> ParamsDefaultÉrtékekkel -egy 1
Paraméterek megadott értékekkel:
Key Value
--- -----
egy 1
Paraméterek default értékekkel:
Name Value
---- -----
üres
kettő 2
Érdekes, erről a kompatibilitási különbségről nem tudtam.