1

我正在考虑编写一些 PowerShell 代码,这些代码既可以立即执行,也可以生成将作为生成脚本执行的命令。

我想避免这种情况:

if($Generating){
  write-Output "somecommand.exe"
} 
else{
  somecommand.exe  
}

我开始研究 ScriptBlocks,它起初看起来很有希望,因为我可以将 ScriptBlock 的内容写入控制台而不执行它。如:

$sc = { somecommand.exe }
$sc
 somecommand.exe

我的具体问题是,如果我的脚本块包含参数,当我将脚本块内容写入控制台但不调用脚本块时,我可以让它们解析吗?

例如给定以下脚本块:

$b2 = { Param([string]$P) Write-Host "$P" }

当我在控制台输入“$b2”并回车时,我看到了:

Param([string]$P) Write-Host "$P"

我想看到的是这个(如果参数值为“Foo”):

Param([string]$P) Write-Host "Foo"

我意识到这可以在调用它时完成,无论是通过“&”还是使用 Invoke(),但是有没有办法让参数在不调用的情况下解析,从而使我的脚本生成更加优雅而不需要一堆条件整个代码中的语句?

4

5 回答 5

3

在 PowerShell v3 中,您可以通过 AST 属性获取参数信息,例如:

PS> $sb = {param($a,$b) "a is $a b is $b"}
PS> $sb.Ast.ParamBlock

Attributes           Parameters          Extent              Parent
----------           ----------          ------              ------
{}                   {$a, $b}            param($a,$b)        {param($a,$b) "a...
于 2013-01-24T16:27:10.443 回答
2

适用于 PowerShell v2 的解决方案:

# given the script block
$b2 = { Param([string]$P) Write-Host "$P" }

# make a function of it and "install" in the current scope
Invoke-Expression "function tmp {$b2}"

# get the function and its parameters
(Get-Command tmp).Parameters
于 2013-01-24T17:03:35.780 回答
1

当显示带有双引号 @ "的 here-string 时,它会扩展变量。对于不应扩展的变量,使用反引号 ( ` ) 转义变量。

所以试试这个:

$P = "Foo"

$b2 = @"
{ Param([string]`$P) Write-Host "$P" }
"@

测试:

PS-ADMIN > $b2
{ Param([string]$P) Write-Host "Foo" }

如果您想再次将其转换为 scriptblock-type:

#Convert it into scriptblock
$b3 = [Scriptblock]::Create($b2)

PS-ADMIN > $b3
{ Param([string]$P) Write-Host "Foo" }

PS-ADMIN > $b3.GetType().name
ScriptBlock
于 2013-01-24T16:26:37.757 回答
1

使用一些我认为我已经找到满足我需求的最佳解决方案的建议。考虑以下代码

function TestFunc
{
    Param(
        [Parameter(Mandatory=$true)]
        [string]$Folder,

        [Parameter(Mandatory=$true)]
        [string]$Foo
    )    

    $code = @"
Write-Host "This is a folder $Folder"
Write-Host "This is the value of Foo $Foo"
"@

    $block = [Scriptblock]::Create($code)

    Write-Host "Running the block"  -BackgroundColor Green -ForegroundColor Black
    &$block

    Write-Host "Displaying block code" -BackgroundColor Green -ForegroundColor Black
    $block

}

它的输出:

Running the block
This is a folder c:\some\folder
This is the value of Foo FOOFOO
Displaying block code
Write-Host "This is a folder c:\some\folder"
Write-Host "This is the value of Foo FOOFOO"

通过这种方式,我仍然可以获得保留现有函数及其参数、参数验证、CBH 等的所有好处。我还可以轻松地生成函数将执行或让它执行的代码。感谢所有的投入,这绝对是一次很好的学习经历。

于 2013-01-25T13:25:38.167 回答
0

如果要将块表示为块而不是字符串,则可以使用以下方法:

$printable = invoke-expression ('"' + ($block -replace '"', '`"') + '"')

本质上,您将所有内容都用引号括起来,然后将其作为表达式调用。该-replace调用确保块本身中的任何引号都被转义。

我在这个方便的函数中使用它,如果调用的命令失败,它也会停止执行。

# usage: exec { dir $myDir }
function exec($block)
{
    # expand variables in block so it's easier to see what we're doing
    $printable = invoke-expression ('"' + ($block -replace '"', '`"').Trim() + '"')
    write-host "# $printable" -foregroundcolor gray

    & $block
    if ($lastExitCode -ne 0)
    {
        throw "Command failed: $printable in $(pwd) returned $lastExitCode"
    }
}
于 2013-06-13T07:42:23.303 回答