Как включить локально определенную функцию при использовании команды Invoke-Command PowerShell для удаленного взаимодействия?
У меня есть скрипт ps1, в котором определена функция. Он вызывает функцию, а затем пытается использовать ее удаленно:
function foo
{
Param([string]$x)
Write-Output $x
}
foo "Hi!"
Invoke-Command -ScriptBlock { foo "Bye!" } -ComputerName someserver.example.com -Credential someuser@example.com
этот короткий пример сценария печатает " Привет!"а затем происходит сбой "термин" foo " не распознается как имя командлета, функции, файла сценария или действующей программы."
Я понимаю, что функция не определена на удаленном сервере, потому что он не находится в ScriptBlock. Я мог бы пересмотреть его там, но предпочел бы не делать этого. Я хотел бы определить функцию один раз и использовать ее локально или удаленно. Есть ли хороший способ сделать это?
4 ответов
вам нужно передать саму функцию (не вызов функции в ScriptBlock
).
у меня была такая же потребность только на прошлой неделе и нашел это так обсуждение
таким образом, ваш код станет:
Invoke-Command -ScriptBlock ${function:foo} -argumentlist "Bye!" -ComputerName someserver.example.com -Credential someuser@example.com
обратите внимание, что с помощью этого метода вы можете передавать параметры в свою функцию только позиционно; вы не можете использовать именованные параметры, как при локальном запуске функции.
вы можете передать определение функции в качестве параметра, а затем переопределить функцию на удаленном сервере, создав блок скриптов, а затем точечный источник:
$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!"
}
это устраняет необходимость иметь дубликат вашей функции. Вы также можете передать более одной функции таким образом, если вы так склонны:
$allFunctionDefs = "function foo { ${function:foo} }; function bar { ${function:bar} }"
вы также можете поместить функцию(ы), а также скрипт в файл (foo.ps1) и передайте это для вызова-команды, используя параметр FilePath:
Invoke-Command –ComputerName server –FilePath .\foo.ps1
файл будет скопирован на удаленные компьютеры и выполнен.
хотя это старый вопрос, я хотел бы добавить свое решение.
забавно, что список параметров scriptblock в функциональном тесте не принимает аргумент типа [scriptblock] и поэтому нуждается в преобразовании.
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
еще одна возможность-передать хэш-таблицу в наш блок скриптов, содержащий все методы, которые вы хотели бы иметь в удаленном сеансе:
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