web-dev-qa-db-ja.com

PowerShellで別の関数から関数を呼び出す

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」という用語は、コマンドレット、関数、スクリプトファイル、または操作可能なプログラムの名前として認識されません。名前のスペルを確認するか、パスが含まれていた場合は、パスが正しいことを確認して再試行してください。
 + CategoryInfo:ObjectNotFound:(logMessage:String)[]、CommandNotFoundException 
 + FullyQualifiedErrorId :CommandNotFoundException 
 + PSComputerName:[localhost] 

ファイル内でlogMessage関数を移動してみましたが、グローバルスコープも試しました。

他の言語では、他の関数からlogMessageを呼び出すことができます。それが関数の目的だからです。

コードブロックを再利用する「ワークフローの方法」とは何ですか?

ワークフローに読み込まれるロギングモジュールを作成する必要がありますか?

6
Malcont3nt

以下のように、ワークフロー内で関数と関数呼び出しを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ブロックではなく、ワークフローの外側またはワークフローの内側の2つの関数を定義する場合も、同じことが起こります。

次に、これを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

並列で実行されたため、出力順序がランダムであることがわかります(予想通り)。

3
jkdba

Powershellでは、使用前に関数を定義する必要があります(「レキシカルスコープ」)。あなたの例では、定義する前にlogMessage関数を呼び出しています。

また、例をPowershellワークフローとして構造化しました。ワークフローには、通常のスクリプトにはないいくつかの制限があります。これらの違いに注意する必要があります。私は this search を実行して、違いの説明と議論を見つけました。 最初の「ヒット」 は、適切な情報を提供します。ワークフローで関数を定義できるかどうかについては(まだ)何もわかりませんが、そもそも関数(またはワークフロー)内で関数を定義することには非常に注意が必要です。

2
Jeff Zeitlin

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
1
JosefZ