web-dev-qa-db-fra.com

Appel d'une fonction à partir d'une autre fonction dans PowerShell

La première fois dans PowerShell 5, je ne parviens pas à appeler une fonction permettant d'écrire des messages dans un fichier à partir d'une autre fonction. Ce qui suit est une version simplifiée de ce que je fais.

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

Voici l'erreur qu'il génère:

 Le terme 'logMessage' n'est pas reconnu en tant que nom d'une applet de commande, fonction, fichier de script ou programme utilisable. Vérifiez l'orthographe du nom ou, si un chemin a été inclus, vérifiez que le chemin est correct et essayez à nouveau. : CommandNotFoundException 
 + PSComputerName: [hôte local] 

J'ai essayé de déplacer la fonction logMessage dans le fichier et j'ai même essayé la portée globale.

Dans n'importe quelle autre langue, je pourrais appeler logMessage à partir de n'importe quelle autre fonction. Comme c'est le but d'une fonction.

Quelle est la "manière de flux de travail" de la réutilisation d'un bloc de code?

Dois-je créer un module de journalisation chargé dans le flux de travail?

4
Malcont3nt

Vous pouvez déplacer les fonctions et les appels de fonctions vers un InlineScript (PowerShell ScriptBlock) dans le flux de travail, comme ci-dessous.

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

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

Serait sortie:

Func 1
logMessage

Comme @JeffZeitlin l'a mentionné dans sa réponse, les flux de travail ne sont pas PowerShell et sont beaucoup plus restrictifs. Le bloc InlineScript permet d'interpréter le code PowerShell normal, mais sa portée sera liée au bloc InlineScript. Par exemple, si vous définissez les fonctions dans le bloc de script, puis essayez d'appeler la fonction func1 en dehors du bloc InlineScript (mais toujours dans le flux de travail), elle échouera car elle est hors de portée.

La même chose se produirait si vous définissiez les deux fonctions en dehors du flux de travail ou à l'intérieur du flux de travail, mais pas dans un bloc InlineScript.

Maintenant, pour un exemple d'application de cette méthode à l'exécution d'une boucle 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
        }
    }
}

Exemple d'appel à ce flux de travail ressemblerait à ceci

 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

Vous remarquerez (et comme prévu) que l'ordre de sortie est aléatoire car il a été exécuté en parallèle.

2
jkdba

Powershell exige que les fonctions soient définies avant utilisation ('portée lexicale'). Dans votre exemple, vous appelez la fonction logMessage avant de l'avoir définie.

Vous avez également structuré votre exemple en tant que flux de travail Powershell. Les flux de travail ont des restrictions que les scripts ordinaires n'ont pas; vous devez être conscient de ces différences. J'ai fait cette recherche pour trouver des descriptions et des discussions sur les différences; le premier "hit" fournit une bonne information. Je n'ai (encore) rien trouvé qui puisse dire si les fonctions peuvent être définies dans les flux de travail, mais je me méfierais beaucoup de la définition de fonctions dans des fonctions (ou des flux de travail) en premier lieu.

2
Jeff Zeitlin

Votre fonction logMessage n'est pas visible dans la fonction func1. Il est valide même si la fonction logMessage est déclarée au-dessus de func1 un.

Pour ce cas simple, vous pouvez utiliser fonctions imbriquées comme suit:

workflow test {

    function func1 {

        function logMessage {
            Write-Output "logMessage"
        }

        Write-Output "Func 1"
        logMessage
    }

  func1

}

test

Sortie :

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