Powershell 从脚本中获取函数列表

Powershell 从脚本中获取函数列表,powershell,Powershell,如果我有一个带有以下函数的.ps1文件 function SomeFunction {} function AnotherFunction {} get-command -module scriptfile function SomeFunction function AnotherFunction 如何获取所有这些函数的列表并调用它们 我想这样做: $functionsFromFile = Get-ListOfFunctions -Path 'C:\someScript.ps1' fo

如果我有一个带有以下函数的.ps1文件

function SomeFunction {}

function AnotherFunction {}
get-command -module scriptfile

function SomeFunction
function AnotherFunction
如何获取所有这些函数的列表并调用它们

我想这样做:

$functionsFromFile = Get-ListOfFunctions -Path 'C:\someScript.ps1'
foreach($function in $functionsFromFile)
{
   $function.Run() #SomeFunction and AnotherFunction execute
}

您可以使用
getchilditem
检索所有函数并将它们存储到变量中。然后将脚本加载到to运行空间并再次检索所有函数,然后使用
Where-Object
cmdlet通过排除以前检索到的所有函数来过滤所有新函数。最后迭代所有新函数并调用它们:

$currentFunctions = Get-ChildItem function:
# dot source your script to load it to the current runspace
. "C:\someScript.ps1"
$scriptFunctions = Get-ChildItem function: | Where-Object { $currentFunctions -notcontains $_ }

$scriptFunctions | ForEach-Object {
      & $_.ScriptBlock
}

我需要从多功能脚本中获取函数的名称。这就是我想到的。基于此,也许有人可以提供一个较短的版本

# Get text lines from file that contain 'function' 
$functionList = Select-String -Path $scriptName -Pattern "function"

# Iterate through all of the returned lines
foreach ($functionDef in $functionList)
{
    # Get index into string where function definition is and skip the space
    $funcIndex = ([string]$functionDef).LastIndexOf(" ") + 1

    # Get the function name from the end of the string
    $FunctionExport = ([string]$functionDef).Substring($funcIndex)

    Write-Output $FunctionExport
}

我提出了一个较短的版本来查找和列出脚本列表中的函数。它不是完美的,并且会有问题,因为模式只是单词“Function”,如果这个方法假设它在任何地方找到了一个函数,那么它就会找到关键字

为了遍历文件并获取列表,我使用了“get ChildItem”函数,并使用递归选项传递了路径和文件过滤器规范

它通过管道传递到“Select String”中,并查找“Function”关键字。它不区分大小写,将接受“Function”或“Function”。如果需要,可以添加“-区分大小写”选项

然后迭代输出以获得实际的函数名。“Line”成员是一种字符串类型,我使用了从位置9开始的“Substring”选项,它是刚刚通过“function”标识符的长度

$scriptPath = "c:\\Project"
$scriptFilter = "*.ps1"

( Get-ChildItem -Path $scriptPath -Filter $scriptFilter -Recurse | Select-String -Pattern "function" ) | %{ $_.Line.Substring(9) }

解决方案1.

如果需要使用另一个脚本文件中的函数,可以使用cmdlet导入该函数

函数。ps1包含完整的函数。此脚本需要由主脚本导入

function Write-TextColor
{
    Param(
        [parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
        [ValidateNotNullOrEmpty()]
        [Object]
        $Info,

        [parameter(Position=1, ValueFromPipeline=$true)]
        [ValidateNotNullOrEmpty()]
        [System.ConsoleColor]
        $ForegroundColor =  [System.ConsoleColor]::White,

        [parameter(Position=2, ValueFromPipeline=$true)]
        [ValidateNotNullOrEmpty()]
        [Switch]
        $NoNewLine
        )

        Process{
            foreach ($value in $Info)
            {
                if($NoNewLine)
                {
                    Write-Host $value -ForegroundColor $ForegroundColor -NoNewline
                }
                else {
                    Write-Host $value -ForegroundColor $ForegroundColor
                }
            }            
        }
}
function Write-InfoBlue 
{
    Param(
        [parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
        [ValidateNotNullOrEmpty()]
        [Object]
        $Information,

        [parameter(Position=1, ValueFromPipeline=$true)]
        [Switch]
        $NoNewLine
        )

    Process{
        Write-TextColor $Information Blue $NoNewLine
    }
}
Main.ps1

Import-Module -Name "$($PSCommandPath | Split-Path)/Functions.ps1" -Force
Write-InfoBlue "Printed from imported function."
$FX_ALL_DEFS = Get-AmalgamatedScriptFunctionDefinitions -Path "$($PSCommandPath | Split-Path)/Functions.ps1"
$R_HOST = "192.168.211.1" 
$R_USERNAME = "root" 
$R_PORT = "2222" 
$R_SESSION = New-PSSession -HostName $R_USERNAME@$($R_HOST):$R_PORT #//Connected by OpenSSL, private key added to OpenSSH Session Agent. If you need login by password, remove the private key from OpenSSH Session Agent and write as follows user:pass@host:port $($R_USERNAME):$R_PASS@$($R_HOST):$R_PORT
Invoke-Command -ArgumentList $FX_ALL_DEFS,"Joma" -Session $R_SESSION -ScriptBlock{ #// -ArgumentList function definitions and a name.
    Param($fxs, $name) #// Param received by remote context.
    . ([System.Management.Automation.ScriptBlock]::Create($fxs)) #//Creating function definitions in remote script context.

    Clear-Host
    Write-Host "Running commands in my remote Linux Server" -ForegroundColor Green #//Powershell Core cmdlet
    #//We can use Write-InfoBlue on this script context.
    Write-InfoBlue ($(Get-Content /etc/*-release | Select-String -Pattern "^PRETTY_NAME=.*" ).ToString().Split("=")[1]) #//Created function + cmdlets combo
    Write-InfoBlue $(uname -a) #//Created function + Native remote command
    Write-InfoBlue $(whoami) #//Cmdlet + Native remote command
    printf "Hello! $name" #//Native remote command
    Write-InfoBlue "Local function executed in remote context"
}
Remove-PSSession -Session $R_SESSION
控制台输出


解决方案2.

函数。ps1包含完整的函数。此脚本需要由主脚本导入。与Solution1的脚本相同

Main.ps1
此脚本包含3个函数。
1. 获取脚本函数名。它返回一个字符串数组,每个元素都是函数名。
2. 获取脚本函数定义。它返回一个字符串数组,每个元素都是完整的函数。
3. 获取合并脚本函数定义。它只返回一个字符串,即连接函数Get-ScriptFunctionDefinitions返回的所有元素的结果。 所有3个都需要相同的参数,即Powershell脚本文件的路径

我们将在此文件上测试这3个函数

此脚本未使用导入模块cmdlet

function Get-ScriptFunctionNames {
    param (
        [parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
        [AllowEmptyString()]
        [AllowNull()]
        [System.String]
        $Path
    )
    Process{
        [System.Collections.Generic.List[String]]$FX_NAMES = New-Object System.Collections.Generic.List[String]

        if(!([System.String]::IsNullOrWhiteSpace($Path)))
        { 
            Select-String -Path "$Path" -Pattern "function" | 
            ForEach-Object {
                [System.Text.RegularExpressions.Regex] $regexp = New-Object Regex("(function)( +)([\w-]+)")
                [System.Text.RegularExpressions.Match] $match = $regexp.Match("$_")
                if($match.Success)
                {
                    $FX_NAMES.Add("$($match.Groups[3])")
                }   
            }    
        }
        return ,$FX_NAMES.ToArray()
    }
}
function Get-ScriptFunctionDefinitions {

    param (
        [parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
        [AllowEmptyString()]
        [AllowNull()]
        [System.String]
        $Path
    )
    Process{
        [System.Collections.Generic.List[String]]$FX_DEFS = New-Object System.Collections.Generic.List[String]
        if(!([System.String]::IsNullOrWhiteSpace($Path)))
        {
            Import-Module -Name "$Path" -Force 
        }
        $names = Get-ScriptFunctionNames -Path $Path
        Get-ChildItem "function:" | Where-Object { $_ -in $names } | ForEach-Object{
            $FX_DEFS.Add("function $($_.Name) { $($_.Definition) };")
        }
        return ,$FX_DEFS.ToArray()
    }
}

function Get-AmalgamatedScriptFunctionDefinitions {

    param (
        [parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
        [AllowEmptyString()]
        [AllowNull()]
        [System.String]
        $Path
    )
    Process{
        [System.String]$FX_DEFS = ""
        Get-ScriptFunctionDefinitions -Path $Path | 
        ForEach-Object {
            $FX_DEFS += "$_$([System.Environment]::NewLine)$([System.Environment]::NewLine)"
        }
        return $FX_DEFS
    }
}
Write-Host
[System.String[]]$FX_NAMES = Get-ScriptFunctionNames -Path "$($PSCommandPath | Split-Path)/Functions.ps1"
[System.String[]]$FX_DEFS = Get-ScriptFunctionDefinitions -Path "$($PSCommandPath | Split-Path)/Functions.ps1"
[System.String] $FX_ALL_DEFS = Get-AmalgamatedScriptFunctionDefinitions -Path "$($PSCommandPath | Split-Path)/Functions.ps1"

. ([System.Management.Automation.ScriptBlock]::Create($FX_ALL_DEFS)) #The functions in Functions.ps1 are created in the current script.
Write-InfoBlue "Printed from imported function."
检查:。

阶级

控制台输出

将以下内容添加到Main.ps1中,我们可以测试这3个函数

Write-Host "• TEST 1" -ForegroundColor Magenta
$FX_NAMES | 
ForEach-Object {
    Write-Host $_
}
Write-Host

Write-Host "• TEST 2" -ForegroundColor Magenta
foreach($value in $FX_DEFS)
{
    Write-Host $value
    Write-Host "███" -ForegroundColor DarkGray
}
Write-Host

Write-Host "• TEST 3" -ForegroundColor Magenta
Write-Host $FX_ALL_DEFS
控制台输出



3。额外解决方案-特殊情况 在获取函数定义时,使用它们调用不包含本地函数定义的远程计算机上的命令非常有用,我们只需获取定义并通过如下参数传递它们

如果需要在远程计算机中运行powershell命令,请安装 在远程计算机上

本地文件
PrintColorFunctions.ps1
与解决方案1的脚本内容相同

本地文件 Main.ps1

Import-Module -Name "$($PSCommandPath | Split-Path)/Functions.ps1" -Force
Write-InfoBlue "Printed from imported function."
$FX_ALL_DEFS = Get-AmalgamatedScriptFunctionDefinitions -Path "$($PSCommandPath | Split-Path)/Functions.ps1"
$R_HOST = "192.168.211.1" 
$R_USERNAME = "root" 
$R_PORT = "2222" 
$R_SESSION = New-PSSession -HostName $R_USERNAME@$($R_HOST):$R_PORT #//Connected by OpenSSL, private key added to OpenSSH Session Agent. If you need login by password, remove the private key from OpenSSH Session Agent and write as follows user:pass@host:port $($R_USERNAME):$R_PASS@$($R_HOST):$R_PORT
Invoke-Command -ArgumentList $FX_ALL_DEFS,"Joma" -Session $R_SESSION -ScriptBlock{ #// -ArgumentList function definitions and a name.
    Param($fxs, $name) #// Param received by remote context.
    . ([System.Management.Automation.ScriptBlock]::Create($fxs)) #//Creating function definitions in remote script context.

    Clear-Host
    Write-Host "Running commands in my remote Linux Server" -ForegroundColor Green #//Powershell Core cmdlet
    #//We can use Write-InfoBlue on this script context.
    Write-InfoBlue ($(Get-Content /etc/*-release | Select-String -Pattern "^PRETTY_NAME=.*" ).ToString().Split("=")[1]) #//Created function + cmdlets combo
    Write-InfoBlue $(uname -a) #//Created function + Native remote command
    Write-InfoBlue $(whoami) #//Cmdlet + Native remote command
    printf "Hello! $name" #//Native remote command
    Write-InfoBlue "Local function executed in remote context"
}
Remove-PSSession -Session $R_SESSION
控制台输出

事实上,当Powershell有办法做到这一点时,为什么要重新发明轮子?我们只需将其制作成一个模块即可:

cp .\scriptFile.ps1 .\scriptfile.psm1
然后导入模块

import-module .\scriptfile.psm1
现在,获取所有函数

function SomeFunction {}

function AnotherFunction {}
get-command -module scriptfile

function SomeFunction
function AnotherFunction

如果您的
.ps1
是一个
.psm1
,那么它将是一个模块,获取函数列表将与
一样简单(导入模块C:\someScript.psm1-Passthru)。ExportedFunctions.Values
。(根据Martin的回答,用
和$\ScriptBlock
调用。)@Jeroenmoster为什么会这样?(我们能把它的名字转换成一个临时文件并运行它吗?)。很好的技巧:-)如果您只想使用特定的函数进行简单的复制/粘贴,请使用:
getchilditem函数:SomeFunction | ForEach对象{“function${”,“$..ScriptBlock,}`n}
。哇!这是一个很好的答案。不知道为什么这不是投票结果。它确实可以打印每个函数的内容,而不需要其他“积垢”。我唯一的愿望是它要短得多,而且更容易使用。我一直在寻找与Bash中的
typeset-f
命令等价的东西。似乎pwsh现在正在进行一些内部字母排序。