2017-08-29 49 views
4

我遇到類似於this question的問題。我想獲得給定的PowerShell腳本中的所有功能,但區別在於我不想執行腳本的內容,也不想執行這些功能。獲取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 
} 
+1

相關:http://www.ravichaganti.com/blog/powershell-ise-addon-ise-function-explorer-using-the-powershell-3-0-parser/ – Matt

回答

4

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的一部分。無論如何,有一種更直接和更靜態的方式來獲得幫助。

而不是定義的功能,你可以使用FunctionDefinitionAst

$helpContent = $functionDefinitions | ForEach-Object { $_.GetHelpContent() } 

這個GetHelpContent方法將返回一個對象CommentHelpInfo每個功能。請注意,這是而不是Get-Help cmdlet返回的相同對象。最值得注意的是它不區分代碼和示例塊中的描述。但是,如果您需要正常分析CBH,則可以獲取註釋塊文本並定義您自己的假版本。

$helpContent = $functionDefinitions | ForEach-Object { 

    # Get the plain string comment block from the AST. 
    $commentBlock = $_.GetHelpContent().GetCommentBlock() 

    # Create a scriptblock that defines a blank version of the 
    # function with the CBH. You may lose some parameter info 
    # here, if you need that replace param() with 
    # $_.Body.ParamBlock.Extent.Text 
    $scriptBlock = [scriptblock]::Create((' 
    function {0} {{ 
     {1} 
     param() 
    }}' -f $_.Name, $commentBlock)) 

    # Dot source the scriptblock in a different scope so we can 
    # get the help content but still not pollute the session. 
    & { 
     . $scriptBlock 

     Get-Help $_.Name 
    } 
} 
+1

這真棒 – Matt

+1

@Matt說,這真棒 – SomeShinyObject

+0

@帕特里克:這太棒了。 –

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 
} 
+0

相信我,我我更喜歡這樣做,而不是這樣,但不幸的是,在我正在進行的項目中,我並不孤單。他們和我的感覺不一樣。所以,當我要求函數從執行中移除時,並不總是會發生。 – SomeShinyObject

相關問題