在PowerShell中从另一个函数调用一个函数

8

第一次使用PowerShell 5,我在调用一个将消息写入文件的函数时遇到了麻烦。以下是我正在做的简化版本。

workflow test {
    function logMessage {
        param([string] $Msg)

        Write-Output $Msg
    }

    function RemoveMachineFromCollection{
        param([string]$Collection, [string]$Machine)

        # If there's an error
        LogMessage "Error Removing Machine"

        # If all is good
        LogMessage "successfully remove machine"
    }


    $Collections = DatabaseQuery1

    foreach -parallel($coll in $Collections) {
        logMessage "operating on $coll collection"

        $Machines = DatabaseQuery2

        foreach($Mach in $Machines) {
            logMessage "Removing $Mach from $coll"

            RemoveMachineFromCollection -Collection $coll -Machine $Mach
        }
    }
}

test

这里是它生成的错误信息:
术语“logMessage”未被识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包含路径,请验证该路径是否正确,然后重试。
    + CategoryInfo          : ObjectNotFound: (logMessage:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException
    + PSComputerName        : [localhost]
我已经尝试在文件中移动 logMessage 函数并尝试全局范围。
在任何其他语言中,我都可以从任何其他函数调用 logMessage。因为这就是函数的目的。
有什么“工作流程方式”可以重用一块代码吗?
我需要创建一个日志记录模块并将其加载到工作流中吗?

我更新了代码块以更好地表示我想要做的事情。这是伪代码。 - Malcont3nt
正如我在我的回答评论中提到的,我可能会选择使用runspaces来实现更轻松的灵活性和控制,以便在PowerShell runspaces中加载您的作用域和内容。 - jkdba
3个回答

3
您可以将函数和函数调用移动到工作流内部的InlineScript(PowerShell ScriptBlock)中,如下所示。
workflow test {
    InlineScript
    {
        function func1{
            Write-Output "Func 1"
            logMessage
        }

        function logMessage{
            Write-Output "logMessage"
        }
        func1
    }
}

输出结果:

Func 1
logMessage

正如 @JeffZeitlin 在他的答案中提到的,工作流程不是 PowerShell,且限制更多。InlineScript 块允许普通的 PowerShell 代码被解释,但作用域将被绑定到 InlineScript 块。例如,如果您在脚本块中定义函数,然后尝试在 InlineScript 块之外(但仍在工作流内部)调用 func1 函数,它将失败,因为它超出了作用域。
如果您将这两个函数定义在工作流之外或工作流内但不在 InlineScript 块中,则同样会发生这种情况。
现在,让我们以一个运行 foreach -parallel 循环的示例来说明如何应用此功能。
workflow test {
    ## workflow parameter
    param($MyList)

    ## parallel foreach loop on workflow parameter
    foreach -parallel ($Item in $MyList)
    {
        ## inlinescript
        inlinescript
        {
            ## function func1 declaration
            function func1{
                param($MyItem)
                Write-Output ('Func 1, MyItem {0}' -f $MyItem)
                logMessage $MyItem
            }

            ## function logMessage declaration
            function logMessage{
                param($MyItem)
                Write-Output ('logMessage, MyItem: {0}' -f $MyItem)
            }
            ## func1 call with $Using:Item statement
            ## $Using: prefix allows us to call items that are in the workflow scope but not in the inlinescript scope.
            func1 $Using:Item
        }
    }
}

这个工作流程的示例调用看起来像这样:
 PS> $MyList = 1,2,3
 PS> test $MyList
     Func 1, MyItem 3
     Func 1, MyItem 1
     Func 1, MyItem 2
     logMessage, MyItem: 3
     logMessage, MyItem: 2
     logMessage, MyItem: 1

你会注意到(这是预期的),输出顺序是随机的,因为它是并行运行的。

我认为这会阻止我并行运行Func1。 - Malcont3nt
@Malcont3nt 不一定,我会用一个 foreach -parallel 循环的例子来更新我的答案。 - jkdba
@Malcont3nt添加了带有inlinescript块的并行示例。 - jkdba
我希望能够在工作流的任何地方调用 LogMessage。虽然可以多次包含 LogMessage 函数,但这违背了函数的目的。 - Malcont3nt
@Malcont3nt 当然,如果是这种情况,我会将我重复使用的函数放在一个ps1文件或PowerShell模块中,并在inlinescript块或需要它的地方进行源或导入。如果这对您来说不是解决方案,那么您需要研究工作流程范围以更好地了解如何实现您所寻找的内容。 - jkdba
显示剩余2条评论

2
Powershell要求在使用前定义函数(“词法作用域”)。在您的示例中,您在定义logMessage函数之前调用它。
您还将示例结构化为Powershell工作流。工作流具有一些普通脚本没有的限制;您需要了解这些差异。我进行了此搜索以查找一些差异的描述和讨论;第一个搜索结果提供了很好的信息。我还没有找到任何关于是否可以在工作流中定义函数的说明,但是我首先会非常谨慎地定义函数内部的函数(或工作流)。

我知道PowerShell和Workflows之间的一些区别。这在我开始使用PowerShell时令我感到困惑。不幸的是,将LogMessage移动到Func1上方并没有帮助我解决问题。我认为这可能与Workflow中的作用域有关。 - Malcont3nt

1
你的logMessage函数在func1函数内部不可见。尽管logMessage函数在func1函数上面声明,但这是有效的。
对于这个简单的情况,你可以使用嵌套函数,如下所示:
workflow test {

    function func1 {

        function logMessage {
            Write-Output "logMessage"
        }

        Write-Output "Func 1"
        logMessage
    }

  func1

}

test

Output:

PS D:\PShell> D:\PShell\SO\41770877.ps1
Func 1
logMessage

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接