5

假设我有MyScript.ps1

[cmdletbinding()]
param ( 
    [Parameter(Mandatory=$true)]
        [string] $MyInput
)

function Show-Input {
    param ([string] $Incoming)
    Write-Output $Incoming
}

function Save-TheWorld {
    #ToDo
}

Write-Host (Show-Input $MyInput)

是否可以仅以某种方式点源函数?问题是如果上面的脚本是点源的,它会执行整个事情......

我最好的选择是使用Get-Content和解析函数并使用 Invoke-Expression ...?或者有没有办法以编程方式访问 PowerShell 的解析器?我看到这可能与 PSv3 一起使用[System.Management.Automation.Language.Parser]::ParseInput,但这不是一个选项,因为它必须在 PSv2 上工作。

我问的原因是我正在尝试Pester PowerShell 单元测试框架,它对函数运行测试的方式是通过点源文件与测试夹具中的函数。测试夹具如下所示:

MyScript.Tests.ps1

$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".")
. "$here\$sut"

Describe "Show-Input" {

    It "Verifies input 'Hello' is equal to output 'Hello'" {
        $output = Show-Input "Hello"
        $output.should.be("Hello")
    }
}
4

2 回答 2

4

使用Doug 的Get-Function函数,您可以通过以下方式包含函数:

$script = get-item .\myscript.ps1
foreach ($function in (get-function $script))
{
  $startline = $function.line - 1
  $endline = $startline
  $successful = $false
  while (! $successful)
  {
    try {
      $partialfunction = ((get-content $script)[$startline..$endline]) -join [environment]::newline
      invoke-expression $partialfunction
      $successful = $true
    }
    catch [Exception] { $endline++ }
  }
}

编辑:[System.Management.Automation.IncompleteParseException] 可以用来代替 Powershell V2 中的 [Exception]。

于 2012-05-11T11:39:05.587 回答
2

注意- 如果您发现此答案有帮助,请投票支持 jonZ 的答案,因为如果不是因为他的有用答案,我将无法提出这个答案。

我基于链接到的脚本@jonZ 创建了这个函数提取器函数。这用于[System.Management.Automation.PsParser]::Tokenize遍历输入脚本中的所有标记并将函数解析为函数信息对象并将所有函数信息对象作为数组返回。每个对象如下所示:

Start       : 99
Stop        : 182
StartLine   : 7
Name        : Show-Input
StopLine    : 10
StartColumn : 5
StopColumn  : 1
Text        : {function Show-Input {,     param ([string] $Incoming),     Write-Output $Incoming, }}

text 属性是一个字符串数组,可以写入临时文件和点源,或者使用换行符组合成一个字符串,并使用Invoke-Expression.

仅提取函数文本,因此如果一行有多个语句,例如:Get-Process ; function foo () {仅提取与函数相关的部分。

function Get-Functions {
    param (
        [Parameter(Mandatory=$true)]
        [System.IO.FileInfo] $File
    )

    try {
        $content = Get-Content $File
        $PSTokens = [System.Management.Automation.PsParser]::Tokenize($content, [ref] $null)

        $functions = @()

        #Traverse tokens.
        for ($i = 0; $i -lt $PSTokens.Count; $i++) {
            if($PSTokens[$i].Type -eq  'Keyword' -and $PSTokens[$i].Content -eq 'Function' ) {
                $fxStart = $PSTokens[$i].Start
                $fxStartLine = $PSTokens[$i].StartLine
                $fxStartCol = $PSTokens[$i].StartColumn

                #Skip to the function name.
                while (-not ($PSTokens[$i].Type -eq  'CommandArgument')) {$i++}
                $functionName = $PSTokens[$i].Content

                #Skip to the start of the function body.
                while (-not ($PSTokens[$i].Type -eq 'GroupStart') -and -not ($PSTokens[$i].Content -eq '{')) {$i++ }

                #Skip to the closing brace.
                $startCount = 1 
                while ($startCount -gt 0) { $i++ 
                    if ($PSTokens[$i].Type -eq 'GroupStart' -and $PSTokens[$i].Content -eq '{') {$startCount++}
                    if ($PSTokens[$i].Type -eq 'GroupEnd'   -and $PSTokens[$i].Content -eq '}') {$startCount--}
                }

                $fxStop = $PSTokens[$i].Start
                $fxStopLine = $PSTokens[$i].StartLine
                $fxStopCol = $PSTokens[$i].StartColumn

                #Extract function text. Handle 1 line functions.
                $fxText = $content[($fxStartLine -1)..($fxStopLine -1)]
                $origLine = $fxText[0]
                $fxText[0] = $fxText[0].Substring(($fxStartCol -1), $fxText[0].Length - ($fxStartCol -1))
                if ($fxText[0] -eq $fxText[-1]) {
                    $fxText[-1] = $fxText[-1].Substring(0, ($fxStopCol - ($origLine.Length - $fxText[0].Length)))
                } else {
                    $fxText[-1] = $fxText[-1].Substring(0, ($fxStopCol))
                }

                $fxInfo = New-Object -TypeName PsObject -Property @{
                    Name = $functionName
                    Start = $fxStart
                    StartLine = $fxStartLine
                    StartColumn = $fxStartCol
                    Stop = $fxStop
                    StopLine = $fxStopLine
                    StopColumn = $fxStopCol
                    Text = $fxText
                }
                $functions += $fxInfo
            }
        }
        return $functions
    } catch {
        throw "Failed in parse file '{0}'. The error was '{1}'." -f $File, $_
    }
}

# Dumping to file and dot sourcing:
Get-Functions -File C:\MyScript.ps1 | Select -ExpandProperty Text | Out-File C:\fxs.ps1
. C:\fxs.ps1
Show-Input "hi"

#Or import without dumping to file:

Get-Functions -File  C:\MyScript.ps1 | % { 
    $_.Text -join [Environment]::NewLine | Invoke-Expression
}
Show-Input "hi"
于 2012-05-11T02:14:48.693 回答