5 votes

Obtenir toutes les fonctions d'un script PowerShell

J'ai un problème similaire à cette question . Je veux obtenir toutes les fonctions d'un script PowerShell donné, mais la différence est que je ne veux pas exécuter le contenu du script et je ne veux pas exécuter les fonctions.

L'objectif est de pouvoir charger toutes les fonctions dans l'espace de travail afin de pouvoir extraire l'aide basée sur les commentaires de chaque fonction à des fins de documentation.

Quelqu'un a-t-il des astuces magiques pour charger les fonctions d'un fichier .ps1 sans exécuter tout le reste du code contenu dans ce fichier ?

J'ai pensé à utiliser [System.Management.Automation.PSParser]::Tokenize() pour analyser le fichier script mais c'est beaucoup plus de travail que ce que je voudrais faire. Si quelqu'un a quelque chose de plus simple, j'en serais ravi.

# 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
}

8voto

Patrick Meinecke Points 2412

L'AST est la voie à suivre pour l'analyse statique. Voici comment je ferais ce que vous avez décrit

$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()
}

Vous pouvez également utiliser la fonction $tokens à partir du haut de la page si vous souhaitez opter pour une solution purement statique. Les commentaires ne seront pas dans l'AST mais ils seront dans les jetons.

Editar La méthode ci-dessus perd en fait l'aide au commentaire quelque part dans le processus, non pas à cause de l'espace d'exécution, mais simplement à cause de la manière dont la fonction est assignée. Probablement parce que les commentaires ne sont pas vraiment faire partie de l'AST. En tout état de cause, il existe un moyen plus direct et plus statique d'obtenir l'aide.

Au lieu de définir les fonctions, vous pouvez utiliser la fonction GetHelpContent méthode sur FunctionDefinitionAst

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

Cela renverra un CommentHelpInfo pour chaque fonction. Il est important de noter qu'il s'agit de pas le même objet que celui retourné par la fonction Get-Help cmdlet. En particulier, il ne fait pas de distinction entre le code et la description dans un bloc d'exemple. Toutefois, si vous souhaitez que le CBH soit analysé normalement, vous pouvez obtenir le texte du bloc de commentaires et définir votre propre fausse version.

$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
    }
}

1voto

johnwik Points 11

Idéalement, vos fonctions devraient se trouver dans un module (ou dans leur propre fichier script), que vous pourriez charger. Votre script "d'exécution" serait alors une chose à part entière que vous n'exécuteriez que lorsque vous voulez exécuter la fonction, ou que vous exécutez les fonctions manuellement.

Si vos fonctions se trouvaient dans un module, dans l'un des chemins que PowerShell recherche, vous seriez en mesure d'exécuter ceci pour voir les fonctions :

Get-Command -Module Example -CommandType Function

La plupart du temps, vous n'incluez pas le paramètre CommandType, à moins que le module ne contienne des éléments supplémentaires dont vous ne vous souciez pas.

Cette approche modulaire (ou séparation de la fonction/exécution) serait le seul moyen de faire fonctionner l'aide basée sur les commentaires comme vous l'attendez.

Si vous souhaitez simplement voir les noms de vos fonctions, vous devez charger le contenu du fichier script et vérifier les lignes commençant par le mot-clé function. Il y a probablement des façons plus intelligentes de faire cela, mais c'est ce qui m'est venu à l'esprit.


Pour être un peu plus clair sur ce que je veux dire à propos de la séparation des fonctions du code d'exécution, cela ressemblerait à quelque chose comme :

fonctions.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"
}

Vous pouvez ensuite charger librement la fonction dans votre session, rendant ainsi accessible l'aide basée sur les commentaires.

execute.ps1

. .\path\functions.ps1

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X