6

First time in PowerShell 5 and I'm having trouble calling a function that writes messages to a file from another function. 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.

In any other language I would be able to call logMessage from any other function. 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?

4

3 回答 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 块。例如,如果您在脚本块中定义函数,然后尝试func1在 InlineScript 块之外(但仍在工作流中)调用该函数,它将失败,因为它超出了范围。

如果您在工作流之外或在工作流内部但不在 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

您会注意到(正如预期的那样)输出顺序是随机的,因为它是并行运行的。

于 2017-01-20T19:40:59.643 回答
2

Powershell 要求在使用之前定义函数(“词法范围”)。在您的示例中,您在logMessage定义函数之前调用了该函数。

您还将示例构建为 Powershell 工作流。工作流有一些普通脚本没有的限制;您需要了解这些差异。我进行了搜索以找到一些关于差异的描述和讨论;第一个“命中”提供了很好的信息。我(还)没有找到任何说明是否可以在工作流中定义函数的信息,但我首先会对在函数(或工作流)中定义函数非常谨慎。

于 2017-01-20T19:36:07.337 回答
1

logMessage从函数内部看不到您的func1函数。即使logMessage函数声明在func1一个之上,它也是有效的。

对于这种简单的情况,您可以使用如下嵌套函数:

workflow test {

    function func1 {

        function logMessage {
            Write-Output "logMessage"
        }

        Write-Output "Func 1"
        logMessage
    }

  func1

}

test

输出

PS D:\PShell> D:\PShell\SO\41770877.ps1
Func 1
logMessage
于 2017-01-20T20:28:36.827 回答