获取.ps1文件中特定函数的最后一行

时间:2014-03-11 20:03:15

标签: function powershell powershell-v3.0

我想获取.ps1文件中特定函数的最后一行。我已经用powershell v3代码完成了这个:

function GetEndLineNumber($ParseFile, $functionName))
{
    $AbstractSyntaxTree = $NewParser::ParseFile($ParseFile, [ref]$null,  [ref]$null)
    $functionsInFile = $AbstractSyntaxTree.FindAll({$args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst]}, $true)
    #endregion

    $initializeFunction = $null
    foreach($function in $functionsInFile)
    {
        if($function.Name -eq $functionName)
        {
            $initializeFunction = $function
            break
        }
    }

    if($initializeFunction -eq $null){ return 0 }

    $initializeFunctionBody = $initializeFunction.Body

    return $initializeFunctionBody.Extent.EndLineNumber
}

但是这个脚本也应该在v2上运行,我尝试使用System.Management.Language.PSParser和ScriptBlock。但没有任何成功。有人知道如何在.ps1文件中获取特定函数名的最后一行(作为int或字符串)?

编辑:

我认为这里有一些误解: 我想在.ps1脚本中获取特定函数的最后一行。不是函数中特定函数的最后一行。因为我必须在最后将自己的代码添加到此函数中 这是一个简单的示例,我的脚本可以看起来如何:

test.ps1

1function test321
2 {
3  Write-Host "test3142"
4 }
5
6function test1
7{
8 if($true)
9 {
10  Write-Host "hello"
11 }
12 #comment
13 
14 Write-host "end"
15 
16}
17
18function test2
19{
20 #bla
21}

我想要一个名为的函数,例如像GetEndLineNumber($ scriptFile,$ functionName),它应该像这样工作:

test2.ps1

$path = "C:\Scripts\test.ps1"
$lastLine = GetEndLineNumber $path "test1"

$lastLine should be in this case 15 or 16.

4 个答案:

答案 0 :(得分:5)

在PowerShell V2中,我们可以使用System.Management.Automation.PSParser并执行一些操作 使用生成的标记解析自己:

function GetEndLineNumber {
    param(
        $scriptFile,
        $functionName
    )

    $code = Get-Content -LiteralPath $scriptFile
    $tokens = [System.Management.Automation.PSParser]::Tokenize($code, [ref]$null)

    $waitForFuncName = $false
    $funcFound = $false
    $level = 0

    foreach($t in $tokens) {
        switch($t.Type) {
            Keyword {
                if ($t.Content -eq 'function') {
                    $waitForFuncName = $true
                }
            }
            CommandArgument {
                if ($waitForFuncName) {
                    $waitForFuncName = $false
                    $funcFound = $t.Content -eq $functionName
                }
            }
            GroupStart {
                ++$level
            }
            GroupEnd {
                --$level
                if ($funcFound -and $level -eq 0 -and $t.Content -eq '}') {
                    return $t.StartLine
                }
            }
        }
    }
}

我没有彻底测试它,但我尝试了一些函数和脚本 返回正确的行号。

答案 1 :(得分:1)

在V3中,对于已存在的函数还有另一个更简单的选项:

$cmd = Get-Command -Name $functionName -CommandType Function,Filter
if ($null -ne $cmd)
{
    return $cmd.ScriptBlock.Ast.Extent.EndLineNumber
}

这适用于使用function / filter关键字以及Set-Item定义的函数,例如

Set-Item -Path function:foo -Value { return 42 }

使用Parser api的原始代码在这种情况下无法正常工作。

在V2中,PSParser api可能是最好的选择 - 但它需要一些稍微棘手的解析。找到'功能后#39;您可以跳过可选参数列表(匹配'('和')')然后您将匹配' {&# 39;和'}'令牌,最后一个没有出现的'}'令牌是你的最后一行。

这不是一个复杂的代码,但它有点棘手,例如如果你没有跳过可选参数列表,你可能会错误地找到匹配的花括号作为默认参数,例如

function foo($a = {}) {}

答案 2 :(得分:0)

这种获取最后一行的方式如何:

首先。输出PS1文件:

. .\Myscript.ps1

然后获取函数的scriptblock:

$myFunction = Get-Item Function:\MyFunction
($myFunction.ScriptBlock ).ToString().split("`n") | select -last 1

正如你可以在@mjolinor的回答中看到的那样,你应该对这种分裂感到骄傲。

答案 3 :(得分:0)

添加到@ JPBlanc的答案,小心那个分裂。它似乎计算花括号之间的所有内容作为脚本块的一部分,包括紧接在之前和之后的任何换行:

 function test {
 'line1'
 'line2'
 'line3'
 }

 $myFunction = Get-Item Function:\test
 ($myFunction.ScriptBlock ).ToString().split("`n").count

 5


 function test {'line1'
 'line2'
 'line3'}

 $myFunction = Get-Item Function:\test
 ($myFunction.ScriptBlock ).ToString().split("`n").count

 3

从第一个元素中取出分割的最后一个元素将返回一个空行。 为了安全起见,我会在你进行拆分之前通过.trim()运行它。你也可以使用$function:<function name>代替Get-Item来缩短它,并使用数组切片代替-Select

function test {
'line1'
'line2'
'line3'
}

$function:test.tostring().trim().split("`n")[-1]

'line3'