#powershell
#powershell
Вопрос:
Предположим, у вас есть следующая функция:
Function Test-Function {
Param (
[String[]]$ComputerNames = @($env:COMPUTERNAME, 'PC2'),
[String]$PaperSize = 'A4'
)
}
Get-DefaultParameterValuesHC -Path 'Test-Function'
Теперь для получения значений по умолчанию в аргументах функции можно использовать AST:
Function Get-DefaultParameterValuesHC {
[OutputType([hashtable])]
Param (
[Parameter(Mandatory)]$Path
)
$ast = (Get-Command $Path).ScriptBlock.Ast
$selectParams = @{
Property = @{
Name = 'Name';
Expression = { $_.Name.VariablePath.UserPath }
},
@{
Name = 'Value';
Expression = { $_.DefaultValue.Extent.Text -replace "`"|'" }
}
}
$result = @{ }
$defaultValueParameters = @($ast.FindAll( {
$args[0] -is [System.Management.Automation.Language.ParameterAst] }
, $true) |
Where-Object { $_.DefaultValue } |
Select-Object @selectParams)
foreach ($d in $defaultValueParameters) {
$result[$d.Name] = foreach ($value in $d.Value) {
$ExecutionContext.InvokeCommand.ExpandString($value)
}
}
$result
}
Проблема здесь в том, что аргумент for $ComputerNames
читается как a string
, в то время как на самом деле это массив string
.
Есть ли способ, которым PowerShell может преобразовать строку в массив? Или, что еще лучше, сначала правильно прочитать значение?
Комментарии:
1. Потому что он возвращает строку
@($env:COMPUTERNAME, 'PC2')
, а не 2 отдельных элемента, как ожидалось.
Ответ №1:
Вам нужно глубже изучить структуру AST…
Я рекомендую вам поиграть с этим PowerShell: графический интерфейс AST Explorer:
Для вашего конкретного примера:
Function Test-Function {
Param (
[String[]]$ComputerNames = @($env:COMPUTERNAME, 'PC2'),
[String]$PaperSize = 'A4'
)
}
$FunctionDefinitionAst = (Get-Command 'Test-Function').ScriptBlock.Ast
$Body = $FunctionDefinitionAst.Body
$ParamBlock = $Body.ParamBlock
$CNParameter = $ParamBlock.Parameters | Where-Object { $_.Name.VariablePath.UserPath -eq 'ComputerNames' }
$DefaultValue = $CNParameter.DefaultValue
$DefaultValue.SubExpression.Statements.PipelineElements.Expression.Elements
VariablePath : env:COMPUTERNAME
Splatted : False
StaticType : System.Object
Extent : $env:COMPUTERNAME
Parent : $env:COMPUTERNAME, 'PC2'
StringConstantType : SingleQuoted
Value : PC2
StaticType : System.String
Extent : 'PC2'
Parent : $env:COMPUTERNAME, 'PC2'
Комментарии:
1. Это намного чище, чем то, что я сделал. Спасибо, железо, ценю помощь.
Ответ №2:
Это немного хакерское решение, но это то, что я придумал, чтобы решить проблему отсутствия возврата array
string
:
Function Get-DefaultParameterValuesHC {
Param (
[Parameter(Mandatory)]$Path
)
$ast = (Get-Command $Path).ScriptBlock.Ast
$selectParams = @{
Property = @{
Name = 'Name';
Expression = { $_.Name.VariablePath.UserPath }
},
@{
Name = 'Value';
Expression = {
if ($_.DefaultValue.StaticType.BaseType.Name -eq 'Array') {
$_.DefaultValue.SubExpression.Extent.Text -split ',' |
ForEach-Object { $_.trim() -replace "`"|'" }
}
else {
$_.DefaultValue.Extent.Text -replace "`"|'"
}
}
}
}
$result = @{ }
$defaultValueParameters = @($ast.FindAll( {
$args[0] -is [System.Management.Automation.Language.ParameterAst] }
, $true) |
Where-Object { $_.DefaultValue } |
Select-Object @selectParams)
foreach ($d in $defaultValueParameters) {
$result[$d.Name] = foreach ($value in $d.Value) {
$ExecutionContext.InvokeCommand.ExpandString($value)
}
}
$result
}
Ответ №3:
ExpandPath
будет расширять переменные только внутри строк. Чтобы получить фактические значения (а не только определение), вы можете использовать Invoke-Expression:
function Get-DefaultParameterValuesHC {
[OutputType([hashtable])]
Param (
[Parameter(Mandatory)]$Path
)
$result = @{ }
(Get-Command $Path).ScriptBlock.Ast.Body.ParamBlock.Parameters | where {$_.DefaultValue} | foreach {
$result[$_.Name.VariablePath.UserPath] = Invoke-Expression $_.DefaultValue.Extent.Text
}
$result
}
ПРИМЕЧАНИЕ: это фактически вызовет объявление по умолчанию, поэтому будет выполняться любая логика внутри этого выражения, как и при запуске функции. Например, значение по умолчанию $Parameter = (Get-Date)
всегда будет вызываться Get-Date
.
Было бы предпочтительнее создать функцию, которая возвращает только объявления по умолчанию и позволяет пользователю решать, вызывать выражение или нет:
function Get-DefaultParameterDeclarations {
Param (
[Parameter(Mandatory, Position = 0)]
[string]$CommandName
)
(Get-Command $CommandName).ScriptBlock.Ast.Body.ParamBlock.Parameters | where {$_.DefaultValue} |
foreach {
[PSCustomObject]@{
Name = $_.Name.VariablePath.UserPath
Expression = $_.DefaultValue.Extent.Text
}
}
}
# get the declarations and (optionally) invoke the expressions:
Get-DefaultParameterDeclarations 'Test-Function' |
select Name, @{n="DefaultValue"; e={Invoke-Expression $_.Expression}}