简体   繁体   English

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

[英]Calling a Function From Another Function in PowerShell

First time in PowerShell 5 and I'm having trouble calling a function that writes messages to a file from another function. 第一次在PowerShell 5中,我无法调用将消息写入另一个函数的文件的函数。 The following is a simplified version of what I'm doing. 以下是我正在做的简化版本。

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

Here's the error it generates: 这是它产生的错误:

The term 'logMessage' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
    + CategoryInfo          : ObjectNotFound: (logMessage:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException
    + PSComputerName        : [localhost]

I've tried moving the logMessage function around in the file and even tried Global scope. 我已经尝试在文件中移动logMessage函数,甚至尝试过Global scope。

In any other language I would be able to call logMessage from any other function. 在任何其他语言中,我都可以从任何其他函数调用logMessage。 As that's the purpose of a function. 因为这是一个功能的目的。

What's the "Workflow way" of reusing a block of code? 重用一段代码的“工作流方式”是什么?

Do I need to create some logging module that gets loaded into the Workflow? 我是否需要创建一些加载到工作流中的日志记录模块?

You could move the functions and function call to an InlineScript (PowerShell ScriptBlock) inside the workflow like below. 您可以将函数和函数调用移动到工作流内的InlineScript (PowerShell ScriptBlock),如下所示。

workflow test {
    InlineScript
    {
        function func1{
            Write-Output "Func 1"
            logMessage
        }

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

Would Output: 输出:

Func 1
logMessage

As @JeffZeitlin mentioned in his answer, workflows are not PowerShell and are much more restrictive. 正如@JeffZeitlin在他的回答中提到的,工作流程不是PowerShell,而是更具限制性。 The InlineScript block allows for normal PowerShell code to be interpreted however the scope will be tied to the InlineScript block. InlineScript块允许解释普通的PowerShell代码,但范围将与InlineScript块相关联。 For instance, if you define the functions in the script block then attempt to call the func1 function outside of the InlineScript block (but still within the workflow) it will fail because it is out of scope. 例如,如果您在脚本块中定义函数然后尝试在InlineScript块之外调用func1函数(但仍在工作流中),它将失败,因为它超出了范围。

The same would happen if you define the two functions either outside of the workflow or inside of the workflow but not in an InlineScript block. 如果您在工作流程之外或工作流程内部而不是在InlineScript块中定义两个函数,则会发生相同的情况。

Now for an example of how you can apply this to running a foreach -parallel loop. 现在举例说明如何将其应用于运行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
        }
    }
}

Example call to this workflow would look like this 对此工作流程的示例调用如下所示

 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

You will notice (and as expected) the output order is random since it was run in parallel. 您将注意到(并且如预期的那样)输出顺序是随机的,因为它是并行运行的。

Powershell requires that functions be defined before use ('lexical scope'). Powershell要求在使用前定义函数('词法范围')。 In your example, you are calling the logMessage function before you have defined it. 在您的示例中,您在定义logMessage函数之前调用它。

You have also structured your example as a Powershell workflow. 您还将示例结构化为Powershell工作流程。 Workflows have some restrictions that ordinary scripts do not; 工作流有一些普通脚本没有的限制; you need to be aware of those differences. 你需要意识到这些差异。 I did this search to find some descriptions and discussions of the differences; 我做了这个搜索 ,找到了一些差异的描述和讨论; the first "hit" provides good information. 第一个“命中”提供了很好的信息。 I have not (yet) found anything saying whether functions can be defined in workflows, but I would be very wary of defining functions within functions (or workflows) in the first place. 我还没有找到任何关于是否可以在工作流中定义函数的内容,但我会非常谨慎地在函数(或工作流)中定义函数。

Your logMessage function is not visible from within func1 function. func1函数logMessage函数。 It's valid even though logMessage function is declared above func1 one. 即使logMessage函数在func1之上声明,它也是有效的。

For this simple case, you could use nested functions as follows: 对于这个简单的情况,您可以使用嵌套函数 ,如下所示:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM