通过变量执行cmdlet?

Execute cmdlet through variable?

我想使用 if \ else 语句来确定要 运行 的 cmdlet,同时为两个命令保持相同的参数:

例如我有这个电话:

    New-AzureRmResourceGroupDeployment `
        -ResourceGroupName $ResourceGroup `
        -TemplateFile $TemplateUri `
        -TemplateParameterFile $TemplateParamFile 

但是我想用一个变量来确定动词:

    $myVerb = if ($booleanTest) {"Test"} else {"New"}
    [$myVerb]-AzureRmResourceGroupDeployment `
         -ResourceGroupName $ResourceGroup `
         -TemplateFile $TemplateUri `
         -TemplateParameterFile $TemplateParamFile 

或者像这样:

    $command = if ($booleanTest) {"Test-AzureRmResourceGroupDeployment"} else {"New-AzureRmResourceGroupDeployment"}
    $command `
         -ResourceGroupName $ResourceGroup `
         -TemplateFile $TemplateUri `
         -TemplateParameterFile $TemplateParamFile 

我尝试了 $command 版本,但失败了:

At C:\Users\Administrator\Dropbox\projects\deloitte\Suncore\Dev\scripts\az-d eploy.ps1:36 char:13 + -ResourceGroupName $ResourceGroup + ~~~~~~~~~~~~~~~~~~ Unexpected token '-ResourceGroupName' in expression or statement. At C:\Users\Administrator\Dropbox\projects\deloitte\Suncore\Dev\scripts\az-d eploy.ps1:36 char:32 + -ResourceGroupName $ResourceGroup + ~~~~~~~~~~~~~~

要准确执行您所描述的操作,您需要将整个命令包装为一个字符串,然后使用 Invoke-Expression 调用它。例如:

$MyCommand = "$myVerb-AzureRmResourceGroupDeployment -ResourceGroupName $ResourceGroup -TemplateFile $TemplateUri"
Invoke-Expression $MyCommand

但我认为这不是一种非常清晰的脚本编写方式。更好的选择是使用 Splatting,您可以在其中创建参数的哈希表,然后您可以通过带有变量名称的特殊 @ 字符发送 cmdlet。例如:

$AzureParams = @{ 
    ResourceGroupName = $ResourceGroup
    TemplateFile = $TemplateUri
    TemplateParameterFile = $TemplateParamFile 
}

If ($booleanTest) {
    Test-AzureRmResourceGroupDeployment @AzureParams
} Else {
    New-AzureRmResourceGroupDeployment @AzurParams
}

这也有避免使用反引号字符的好处,通常鼓励使用反引号字符,因为它很难被发现且容易被破坏。

我不建议在其他答案上使用它,但要直接回答您的问题,请添加调用运算符 &

$command = if ($booleanTest) {
    "Test-AzureRmResourceGroupDeployment"
} else {
    "New-AzureRmResourceGroupDeployment"
}

& $command `
     -ResourceGroupName $ResourceGroup `
     -TemplateFile $TemplateUri `
     -TemplateParameterFile $TemplateParamFile 

使用Splatting as suggested by Mathias R. Jessen:

Function Do-AzureRmResourceGroupDeployment ([ValidateSet("Test", "New")]$Verb, $ResourceGroupName, $TemplateFile, $TemplateParameterFile) {
    $PSBoundParameters.Remove("Verb")
    & "$Verb-AzureRmResourceGroupDeployment" @PSBoundParameters 
}

补充现有的有用答案:

  • Invoke-Expression 应始终是最后 手段,此处不需要。使用 Invoke-Expression,要获得正确的引用是很棘手的,并且它的使用可能存在安全风险(执行作为字符串传递的任意命令,类似于 POSIX-like shell 中的 eval ).

  • Splatting (Get-Help about_Splatting) 永远值得考虑:

    • 它是比多行参数传递更强大的替代方法,如所解释的那样。
    • 允许参数值集的增量、条件构造以及它们在多次调用中的使用
  • 然而,在手头的案例中,由于 只有 命令名称 是可变的 based on &, the call operator is simplest (see Get-Help about_Operators).


通常,只要命令名称 不是 未加引号的文字[=,您就需要 & 90=](例如,notepad foo.txt 有效,但 'notepad' foo.txt 无效)。

换句话说:你需要&,如果你的命令名称是:

  • 引用(无论是'...'还是"...");例如,'notepad'
  • 或者是一个变量引用;例如,$cmdName
  • 或者是表达式的结果(例如,('get' + '-item')
在这些情况下需要

& 以告诉 PowerShell 您的意图是调用 command 而不是评估 expression;如果没有 &,这样的标记将被解释为开始 expression;请参阅 Get-Help about_Parsing 以了解 PowerShell 的两种基本解析模式,参数模式表达式模式 .


虽然它可能不是最易读的解决方案,但您甚至可以将可扩展字符串与嵌入的子表达式($(...) - 再次参见 Get-Help about_Operators)结合起来,无需一个辅助。变量:

& "$( if ($booleanTest) {'Test'} else {'New'} )-AzureRmResourceGroupDeployment" `
     -ResourceGroupName $ResourceGroup `
     -TemplateFile $TemplateUri `
     -TemplateParameterFile $TemplateParamFile