获取PowerShell脚本中的所有函数

时间:2017-08-29 00:29:52

标签: powershell

我遇到与this question类似的问题。我想获得给定PowerShell脚本中的所有函数,但区别在于我不想执行脚本的内容而我不想执行这些函数。

目的是能够将所有函数加载到运行空间中,以便能够从每个函数中提取基于注释的帮助以用于文档目的。

有没有人有任何神奇的技巧可以从.ps1加载函数而不执行该文件中的所有其他代码?

我考虑使用[System.Management.Automation.PSParser]::Tokenize()来解析脚本文件,但这比我想做的工作要多得多。如果某人有更轻松的事情,我会很高兴。

# I want to load this to get the comment-based help
Function Invoke-Stuff {
    <#
    .SYNOPSIS
       Stuff doer
    .DESCRIPTION
       It does lots of stuff
    .EXAMPLE
       Invoke-Stuff
    #>
    Write-Host "Stuff was done"
}

# But I don't want to execute any of this
$Items = Get-ChildItem
$Items | ForEach-Object {
    Invoke-Stuff
}

2 个答案:

答案 0 :(得分:7)

AST是进行静态(ish)分析的方法。这就是我如何做你所描述的

$rs = [runspacefactory]::CreateRunspace()
$rs.Open()

# Get the AST of the file
$tokens = $errors = $null
$ast = [System.Management.Automation.Language.Parser]::ParseFile(
    'MyScript.ps1',
    [ref]$tokens,
    [ref]$errors)

# Get only function definition ASTs
$functionDefinitions = $ast.FindAll({
    param([System.Management.Automation.Language.Ast] $Ast)

    $Ast -is [System.Management.Automation.Language.FunctionDefinitionAst] -and
    # Class methods have a FunctionDefinitionAst under them as well, but we don't want them.
    ($PSVersionTable.PSVersion.Major -lt 5 -or
    $Ast.Parent -isnot [System.Management.Automation.Language.FunctionMemberAst])

}, $true)

# Add the functions into the runspace
$functionDefinitions | ForEach-Object {
    $rs.SessionStateProxy.InvokeProvider.Item.Set(
        'function:\{0}' -f $_.Name,
        $_.Body.GetScriptBlock()) 
}

# Get help within the runspace.
$ps = [powershell]::Create().AddScript('Get-Help MyFunction')
try {
    $ps.Runspace = $rs
    $ps.Invoke()
} finally {
    $ps.Dispose()
}

如果你想要一个纯粹的静态路线,你也可以使用顶部附近的$tokens。评论不会出现在AST中,但它们会出现在令牌中。

编辑上面的方法实际上在进程中的某处丢失了注释帮助,不是因为运行空间而是因为函数的分配方式。可能由于评论不是真的是AST的一部分。在任何情况下,都有更直接,更静态的方式来获得帮助。

您可以在GetHelpContent

上使用FunctionDefinitionAst方法,而不是定义功能
$helpContent = $functionDefinitions | ForEach-Object { $_.GetHelpContent() }

这将为每个函数返回一个CommentHelpInfo对象。请注意,{strong> <{1}} cmdlet返回的同一对象非常重要。最值得注意的是,它不区分示例块中的代码和描述之类的内容。但是,如果您需要正常解析CBH,您可以获取注释块文本并定义自己的假版本。

Get-Help

答案 1 :(得分:1)

理想情况下,您可以在一个模块(或它们自己的脚本文件)中使用您可以加载的功能。那么你的“执行”脚本就是你自己的事情,你只在你想要执行这个功能时运行,或者你手动运行这些功能。

如果你在一个模块中有你的功能,在PowerShell寻找它们的一个路径中,你就可以运行它来查看这些功能:

Get-Command -Module Example -CommandType Function

大多数情况下,您不会包含CommandType参数,除非模块中有额外的内容您不关心。

这种模块方法(或功能/执行的分离)是让您的评论为基础的帮助工作的唯一方法,因为您希望它能够正常工作。

如果您很高兴看到函数的名称,则必须加载脚本文件的内容,并检查以function关键字开头的行。可能有更明智的方法可以做到这一点,但这就是我的想法。

为了更清楚地了解我在将功能与执行代码分开时所取得的成果,它看起来像是:

<强> functions.ps1

# I want to load this to get the comment-based help
Function Invoke-Stuff {
    <#
    .SYNOPSIS
       Stuff doer
    .DESCRIPTION
       It does lots of stuff
    .EXAMPLE
       Invoke-Stuff
    #>
    Write-Host "Stuff was done"
}

然后,您可以自由地将该功能加载到您的会话中,使基于评论的帮助可以访问。

<强> execute.ps1

. .\path\functions.ps1

# But I don't want to execute any of this
$Items = Get-ChildItem
$Items | ForEach-Object {
    Invoke-Stuff
}