webentwicklung-frage-antwort-db.com.de

Wie füge ich eine lokal definierte Funktion hinzu, wenn Sie den Invoke-Befehl von PowerShell für das Remoting verwenden?

Ich habe das Gefühl, dass mir etwas fehlt, das offensichtlich sein sollte, aber ich kann einfach nicht herausfinden, wie ich das machen kann.

Ich habe ein ps1-Skript, in dem eine Funktion definiert ist. Es ruft die Funktion auf und versucht dann, sie remote zu verwenden:

function foo
{
    Param([string]$x)

    Write-Output $x
}

foo "Hi!"

Invoke-Command -ScriptBlock { foo "Bye!" } -ComputerName someserver.example.com -Credential [email protected]

Dieses kurze Beispielskript druckt "Hi!" stürzt dann ab und sagt "Der Begriff" foo "wird nicht als Name eines Cmdlets, einer Funktion, Skriptdatei oder eines ausführbaren Programms erkannt."

Ich verstehe, dass die Funktion nicht auf dem Remote-Server definiert ist, da sie nicht im ScriptBlock enthalten ist. Ich könnte es dort neu definieren, aber ich möchte es lieber nicht. Ich möchte die Funktion einmal definieren und sie entweder lokal oder remote verwenden. Gibt es eine gute Möglichkeit, dies zu tun?

26
David Hogue

Sie müssen die Funktion selbst übergeben (kein Aufruf der Funktion in der ScriptBlock).

Ich hatte das gleiche Bedürfnis erst letzte Woche und fand diese SO Diskussion

So wird Ihr Code zu:

Invoke-Command -ScriptBlock ${function:foo} -argumentlist "Bye!" -ComputerName someserver.example.com -Credential [email protected]

Beachten Sie, dass Sie mit dieser Methode Parameter nur an Ihre Funktion übergeben können. Sie können benannte Parameter nicht wie bei der lokalen Ausführung der Funktion verwenden.

30
alroc

Sie können die Definition der Funktion als Parameter übergeben und dann die Funktion auf dem Remote-Server neu definieren, indem Sie einen Skriptblock erstellen und dann mit einem Punktsourcing versehen:

$fooDef = "function foo { ${function:foo} }"

Invoke-Command -ArgumentList $fooDef -ComputerName someserver.example.com -ScriptBlock {
    Param( $fooDef )

    . ([ScriptBlock]::Create($fooDef))

    Write-Host "You can call the function as often as you like:"
    foo "Bye"
    foo "Adieu!"
}

Dadurch entfällt die Notwendigkeit einer Kopie Ihrer Funktion. Sie können auch mehrere Funktionen auf diese Weise übergeben, wenn Sie dazu neigen:

$allFunctionDefs = "function foo { ${function:foo} }; function bar { ${function:bar} }"
24
JamesQMurphy

Sie können die Funktion (en) sowie das Skript auch in eine Datei (foo.ps1) legen und diese über den Parameter FilePath an Invoke-Command übergeben:

Invoke-Command –ComputerName server –FilePath .\foo.ps1

Die Datei wird auf die Remote-Computer kopiert und ausgeführt.

4
Keith Hill

Obwohl dies eine alte Frage ist, möchte ich meine Lösung hinzufügen. 

Komischerweise nimmt die param-Liste des Scriptblocks im Funktionstest keine Argumente vom Typ [Scriptblock] an und muss daher konvertiert werden.

Function Write-Log 
{
    param(
        [string]$Message
    )

    Write-Host -ForegroundColor Yellow "$($env:computername): $Message"
}

Function Test
{
    $sb = {
        param(
            [String]$FunctionCall
        )

        [Scriptblock]$WriteLog = [Scriptblock]::Create($FunctionCall) 
        $WriteLog.Invoke("There goes my message...")               
    }

    # Get function stack and convert to type scriptblock 
    [scriptblock]$writelog = (Get-Item "Function:Write-Log").ScriptBlock 

    # Invoke command and pass function in scriptblock form as argument 
    Invoke-Command -ComputerName SomeHost -ScriptBlock $sb -ArgumentList $writelog
}

Test

Eine andere Möglichkeit besteht darin, eine Hashtabelle an unseren Skriptblock zu übergeben, die alle Methoden enthält, die in der Remote-Sitzung verfügbar sein sollen: 

Function Build-FunctionStack 
{
    param([ref]$dict, [string]$FunctionName)

    ($dict.Value).Add((Get-Item "Function:${FunctionName}").Name, (Get-Item "Function:${FunctionName}").Scriptblock)
}

Function MyFunctionA 
{
    param([string]$SomeValue)

    Write-Host $SomeValue
}

Function MyFunctionB
{
    param([int]$Foo)

    Write-Host $Foo
}

$functionStack = @{}

Build-FunctionStack -dict ([ref]$functionStack) -FunctionName "MyFunctionA"
Build-FunctionStack -dict ([ref]$functionStack) -FunctionName "MyFunctionB" 

Function ExecuteSomethingRemote
{
    $sb = {
        param([Hashtable]$FunctionStack)

        ([Scriptblock]::Create($functionStack["MyFunctionA"])).Invoke("Here goes my message");
        ([Scriptblock]::Create($functionStack["MyFunctionB"])).Invoke(1234);

    }

    Invoke-Command -ComputerName SomeHost -ScriptBlock $sb -ArgumentList $functionStack
}

ExecuteSomethingRemote
0
Matze