【问题标题】:Get List Of Functions From Script从脚本中获取函数列表
【发布时间】:2020-10-04 19:00:31
【问题描述】:

如果我有一个具有以下功能的 .ps1 文件

function SomeFunction {}

function AnotherFunction {}

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

我想做这样的事情:

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

【问题讨论】:

  • 如果您的.ps1.psm1,那么它将是一个模块,获取函数列表就像(Import-Module C:\someScript.psm1 -Passthru).ExportedFunctions.Values 一样简单。 (根据 Martin 的回答,使用 &$_.ScriptBlock 调用。)
  • @JeroenMostert 为什么会这样? (我们可以将它的名称转换为临时文件并运行它吗?)

标签: powershell


【解决方案1】:

您可以使用Get-ChildItem 检索所有函数并将它们存储到变量中。然后将脚本加载到运行空间并再次检索所有函数,并使用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-ChildItem function:SomeFunction | ForEach-Object { "function $_ {", $_.ScriptBlock, "}`n" }
【解决方案2】:

• 解决方案 1.

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

Functions.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."

控制台输出


• 解决方案 2。

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

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

我们将测试这个文件的 3 个函数。

此脚本不使用 Import-Module 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."

检查:Dot Sourcing operator.
Dot Source
ScriptBlock

控制台输出

在 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 命令,请安装 Powershell Core 在远程计算机上。

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

本地文件 Main.ps1

$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

控制台输出

【讨论】:

  • 哇!这是一个答案。不知道为什么这不被赞成。它确实打印了每个函数的内容,没有其他“粗鲁”。我唯一的愿望是它更短更容易使用。我正在寻找与 Bash 中的 typeset -f 命令等效的东西。
  • 看来 pwsh 现在也在做一些内部字母排序。
【解决方案3】:

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

# 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”。如果需要,可以通过添加“-CaseSensitive”来区分大小写。

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

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

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

【讨论】:

    【解决方案4】:

    事实上,当 Powershell 有办法做到这一点时,为什么要重新发明轮子呢?我们可以简单地通过使其成为一个模块来做到这一点:

    cp .\scriptFile.ps1 .\scriptfile.psm1
    

    然后导入模块

    import-module .\scriptfile.psm1
    

    现在,获取所有功能

    get-command -module scriptfile
    
    function SomeFunction
    function AnotherFunction
    

    【讨论】:

      猜你喜欢
      • 2021-02-12
      • 1970-01-01
      • 1970-01-01
      • 2021-05-06
      • 2016-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多