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を使用するとこれが可能になる可能性がありますが、PSv2で動作する必要[System.Management.Automation.Language.Parser]::ParseInputがあるため、これはオプションではありません。

私が尋ねている理由は、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++ }
  }
}

編集:Powershell V2では、[Exception]の代わりに[System.Management.Automation.IncompleteParseException]を使用できます。

于 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 に答える