在 PowerShell 的模块中动态创建函数

Creating functions dynamically in a module in PowerShell

假设我在模块中有以下代码(称为 MyModule.psm1,在模块的适当位置):

function new-function{

    $greeting='hello world'
    new-item -path function:\ -name write-greeting -value {write-output $greeting} -Options AllScope
    write-greeting
}

导入模块和运行 new-function 后,我可以成功调用write-greeting 函数(由new-function 创建)。

当我尝试在新函数调用范围之外调用写问候函数时,它失败了,因为该函数不存在。

我试过 dot-sourcing new-function,但这没有帮助。我提供了 -option Allscope,但显然它只包含在子范围中。

我也尝试过在新项目调用后使用导出模块成员写入问候语明确地进行尝试,它不会给出错误,但也不会创建函数。

我希望能够从模块内的函数动态创建函数(即通过 new-item,因为函数的内容和名称会根据输入而变化),并让新创建的函数可供调用在模块之外。

具体来说,我希望能够做到这一点:

Import-module MyModule
New-Function
write-greeting

并看到 "hello world" 作为输出

有什么想法吗?

使函数可见非常简单:只需将 New-Item 中的函数名称更改为具有 global: 作用域修饰符:

new-item -path function:\ -name global:write-greeting -value {write-output $greeting} #-Options AllScope

不过,你的例子会有一个新问题,因为 $greeting 将只存在于 new-function 范围内,当你调用 [=15= 时它不会存在].您正在使用未绑定的脚本块定义模块,这意味着它将在其范围内查找 $greeting (它不会找到它),然后它将在任何父范围内查找。它不会看到来自 new-function 的那个,因此您获得任何输出的唯一方法是模块或全局范围是否包含 $greeting 变量。

我不太确定你真正的动态函数会是什么样子,但解决这个新问题的最简单方法是围绕你的脚本块创建一个新的闭包,如下所示:

new-item -path function:\ -name global:write-greeting -value {write-output $greeting}.GetNewClosure()

这将创建一个新的动态模块,其中包含当时可用的状态副本。当然,这会产生一个新问题,因为如果您调用 Remove-Module MyModule,该函数不会消失。在没有更多信息的情况下,我不确定这对您来说是否有问题...

您接近需要 dot source,但您缺少 Export-ModuleMember。这是一个完整的例子:

function new-function
{
    $greeting='hello world'
    Invoke-Expression "function write-greeting { write-output '$greeting' }"
    write-greeting
}

. new-function

Export-ModuleMember -Function write-greeting

您也不需要或不想要 -Scope AllScope。

使用 global: 范围限定符似乎可行,但不是理想的解决方案。首先,您的函数可能会在全局范围内踩踏另一个函数,而模块通常不应该这样做。其次,如果您删除该模块,您的全局功能将不会被删除。最后 - 您的全局函数不会在模块范围内定义,因此如果它需要访问模块中的 non-exported 函数或变量,您将无法(轻松)获取它们。

多亏了其他解决方案,我才能够想出一个小帮手,让我可以添加纯 script-files 作为函数,并一步将它们导出到模块中。 我已将以下功能添加到我的 .psm1

function AddModuleFileAsFunction {
    param (
        [string] $Name, 
        [switch] $Export
    )

    $content = Get-Content (Join-Path $PSScriptRoot "$Name.ps1") -Raw

    # Write-Host $content

    $expression = @"
function $Name {
$content
}
"@

    Invoke-Expression $expression

    if ($Export) {
        Export-ModuleMember -Function $Name
    }
}

这允许我将脚本作为函数加载:

. AddModuleFileAsFunction "Get-WonderfulThings" -Export

( 加载 Get-WonderfulThings.ps1 正文并将其导出为 function:Get-WonderfulThings )