web-dev-qa-db-de.com

Aufrufen einer Funktion aus einer anderen Funktion in PowerShell

Zum ersten Mal in PowerShell 5 und ich habe Probleme beim Aufrufen einer Funktion, die Nachrichten von einer anderen Funktion in eine Datei schreibt. Das Folgende ist eine vereinfachte Version dessen, was ich tue.

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

Hier ist der Fehler, den es erzeugt:

 Der Begriff 'logMessage' wird nicht als Name eines Cmdlets, einer Funktion, einer Skriptdatei oder eines bedienbaren Programms erkannt. Überprüfen Sie die Schreibweise des Namens oder überprüfen Sie, ob der Pfad korrekt ist, und versuchen Sie es erneut : CommandNotFoundException 
 + PSComputername: [localhost] 

Ich habe versucht, die logMessage-Funktion in der Datei zu verschieben, und habe sogar den globalen Gültigkeitsbereich ausprobiert.

In jeder anderen Sprache könnte ich logMessage von jeder anderen Funktion aus aufrufen. Da dies der Zweck einer Funktion ist.

Was ist der "Workflow-Weg" der Wiederverwendung eines Codeblocks?

Muss ich ein Protokollierungsmodul erstellen, das in den Workflow geladen wird?

4
Malcont3nt

Sie können die Funktionen und den Funktionsaufruf in eine InlineScript (PowerShell ScriptBlock) innerhalb des Workflows verschieben (siehe unten).

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

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

Würde ausgeben:

Func 1
logMessage

Wie @JeffZeitlin in seiner Antwort erwähnte, handelt es sich bei Workflows nicht um PowerShell und sie sind viel restriktiver. Der InlineScript-Block ermöglicht die Interpretation von normalem PowerShell-Code. Der Gültigkeitsbereich wird jedoch an den InlineScript-Block gebunden. Wenn Sie beispielsweise die Funktionen im Skriptblock definieren, versuchen Sie, die Funktion func1 außerhalb des InlineScript-Blocks (aber immer noch innerhalb des Workflows) aufzurufen.

Dasselbe würde passieren, wenn Sie die beiden Funktionen entweder außerhalb des Workflows oder innerhalb des Workflows definieren, jedoch nicht in einem InlineScript-Block.

Nun ein Beispiel, wie Sie dies auf die Ausführung einer foreach -parallel-Schleife anwenden können.

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
        }
    }
}

Ein Beispielaufruf für diesen Workflow würde folgendermaßen aussehen

 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

Sie werden feststellen (und wie erwartet), dass die Ausgabereihenfolge zufällig ist, da sie parallel ausgeführt wurde.

2
jkdba

Powershell erfordert, dass Funktionen vor der Verwendung definiert werden ('lexikalischer Geltungsbereich'). In Ihrem Beispiel rufen Sie die Funktion logMessage auf, bevor Sie sie definiert haben.

Sie haben Ihr Beispiel auch als Powershell-Workflow strukturiert. Für Workflows gelten einige Einschränkungen, die für gewöhnliche Skripts nicht gelten. Sie müssen sich dieser Unterschiede bewusst sein. Ich habe diese Suche um einige Beschreibungen und Diskussionen über die Unterschiede zu finden; der erste "Treffer" liefert gute Informationen. Ich habe (noch) keine Aussage darüber gemacht, ob Funktionen in Workflows definiert werden können, aber ich würde es sehr misstrauisch machen, Funktionen innerhalb von Funktionen (oder Workflows) zu definieren.

2
Jeff Zeitlin

Ihre logMessage-Funktion ist innerhalb der func1-Funktion nicht sichtbar. Sie ist gültig, auch wenn die logMessage-Funktion über func1 eins deklariert ist.

Für diesen einfachen Fall können Sie geschachtelte Funktionen wie folgt verwenden:

workflow test {

    function func1 {

        function logMessage {
            Write-Output "logMessage"
        }

        Write-Output "Func 1"
        logMessage
    }

  func1

}

test

Ausgabe :

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