Как передать именованные параметры с помощью Invoke-Command?
У меня есть script, который я могу запустить удаленно с помощью команды Invoke-Command
Invoke-Command -ComputerName (Get-Content C:\Scripts\Servers.txt) `
-FilePath C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1
Пока я использую параметры по умолчанию, он работает нормально. Однако script имеет 2 параметра [switch] (-Debug и -Clear)
Как передать параметры с помощью команды Invoke-Command? Я пробовал -ArgumentList, но я получаю ошибки, поэтому я должен иметь синтаксис неправильно или что-то в этом роде. Любая помощь приветствуется.
Ответы
Ответ 1
-ArgumentList
основан на использовании команд scriptblock, таких как:
Invoke-Command -Cn (gc Servers.txt) {param($Debug=$False, $Clear=$False) C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 } -ArgumentList $False,$True
Когда вы вызываете его с помощью -File
, он по-прежнему передает параметры, такие как немой сплайсированный массив. Я отправил запрос функции, чтобы добавить это в команду (пожалуйста, проголосуйте за это).
Итак, у вас есть два варианта:
Если у вас есть script, который выглядит так: в сетевом местоположении, доступном с удаленного компьютера (обратите внимание, что -Debug
подразумевается, потому что, когда я использую атрибут Parameter
, script получает CmdletBinding неявно, и, следовательно, все общие параметры):
param(
[Parameter(Position=0)]
$one
,
[Parameter(Position=1)]
$two
,
[Parameter()]
[Switch]$Clear
)
"The test is for '$one' and '$two' ... and we $(if($DebugPreference -ne 'SilentlyContinue'){"will"}else{"won't"}) run in debug mode, and we $(if($Clear){"will"}else{"won't"}) clear the logs after."
Не зацикливаясь на значении $Clear
... если вы хотите вызвать, что вы можете использовать любой из следующих синтаксисов Invoke-Command
:
icm -cn (gc Servers.txt) {
param($one,$two,$Debug=$False,$Clear=$False)
C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 @PSBoundParameters
} -ArgumentList "uno", "dos", $false, $true
В этом я дублирую ВСЕ параметры, которые меня волнуют в скриптблоке, поэтому я могу передавать значения. Если я могу их жестко закодировать (это то, что я на самом деле сделал), нет необходимости делать это и использовать PSBoundParameters
, я могу просто передать те, которые мне нужны. Во втором примере ниже я собираюсь передать $Clear, просто чтобы продемонстрировать, как передавать параметры переключателя:
icm -cn $Env:ComputerName {
param([bool]$Clear)
C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 "uno" "dos" -Debug -Clear:$Clear
} -ArgumentList $(Test-Path $Profile)
Другой вариант
Если script находится на вашем локальном компьютере, и вы не хотите изменять параметры, которые должны быть позиционными, или вы хотите указать параметры, являющиеся общими параметрами (чтобы вы не могли их контролировать), вам нужно будет получить содержимое этого script и вставить его в свой скриптовый блок:
$script = [scriptblock]::create( @"
param(`$one,`$two,`$Debug=`$False,`$Clear=`$False)
&{ $(Get-Content C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 -delimiter ([char]0)) } @PSBoundParameters
"@ )
Invoke-Command -Script $script -Args "uno", "dos", $false, $true
PostScript:
Если вам действительно нужно передать переменную для имени script, то что вы будете делать, будет зависеть от того, определена ли переменная локально или удаленно. В общем случае, если у вас есть переменная $Script
или переменная среды $Env:Script
с именем script, вы можете выполнить ее с помощью оператора вызова (&): &$Script
или &$Env:Script
Если это переменная среды, которая уже определена на удаленном компьютере, все, что ей нужно. Если это локальная переменная, вам нужно передать ее в удаленный блок script:
Invoke-Command -cn $Env:ComputerName {
param([String]$Script, [bool]$Clear)
&$Script "uno" "dos" -Debug -Clear:$Clear
} -ArgumentList $ScriptPath, $(Test-Path $Profile)
Ответ 2
Моим решением было записать динамический блок script с помощью [scriptblock]:Create
:
# Or build a complex local script with MARKERS here, and do substitutions
# I was sending install scripts to the remote along with MSI packages
# ...for things like Backup and AV protection etc.
$p1 = "good stuff"; $p2 = "better stuff"; $p3 = "best stuff"; $etc = "!"
$script = [scriptblock]::Create("MyScriptOnRemoteServer.ps1 $p1 $p2 $etc")
#strings get interpolated/expanded while a direct scriptblock does not
# the $parms are now expanded in the script block itself
# ...so just call it:
$result = invoke-command $computer -script $script
Передача аргументов была очень расстраивающей, пытаясь использовать различные методы, например,
-arguments
, $using:p1
и т.д., и это просто сработало без проблем.
Поскольку я управляю содержимым и расширением переменной строки, которая создает файл [scriptblock]
(или script) таким образом, нет реальной проблемы с заклинанием "invoke-command".
(Это не должно быть так сложно:):)
Ответ 3
Мне нужно что-то, чтобы вызвать скрипты с именованными параметрами. У нас есть политика не использовать порядковое позиционирование параметров и требование имени параметра.
Мой подход аналогичен приведенным выше, но получает содержимое файла script, который вы хотите вызвать, и отправляет блок параметров, содержащий параметры и значения.
Одним из преимуществ этого является то, что вы можете выбрать, какие параметры следует отправлять в файл script, что позволяет использовать необязательные параметры со значениями по умолчанию.
Предполагая, что во временном пути есть script, называемый "MyScript.ps1", который имеет следующий блок параметров:
[CmdletBinding(PositionalBinding = $False)]
param
(
[Parameter(Mandatory = $True)] [String] $MyNamedParameter1,
[Parameter(Mandatory = $True)] [String] $MyNamedParameter2,
[Parameter(Mandatory = $False)] [String] $MyNamedParameter3 = "some default value"
)
Вот как я бы назвал этот script из другого script:
$params = @{
MyNamedParameter1 = $SomeValue
MyNamedParameter2 = $SomeOtherValue
}
If ($SomeCondition)
{
$params['MyNamedParameter3'] = $YetAnotherValue
}
$pathToScript = Join-Path -Path $env:Temp -ChildPath MyScript.ps1
$sb = [scriptblock]::create(".{$(Get-Content -Path $pathToScript -Raw)} $(&{
$args
} @params)")
Invoke-Command -ScriptBlock $sb
Я использовал это во многих сценариях, и он работает очень хорошо.
Одна вещь, которую вам иногда нужно делать, - это поставить кавычки вокруг блока присвоения значения параметра. Это всегда так, когда в значении есть пробелы.
например. Этот блок параметров используется для вызова script, который копирует различные модули в стандартное местоположение, используемое PowerShell C:\Program Files\WindowsPowerShell\Modules
, которое содержит пробельный символ.
$params = @{
SourcePath = "$WorkingDirectory\Modules"
DestinationPath = "'$(Join-Path -Path $([System.Environment]::GetFolderPath('ProgramFiles')) -ChildPath 'WindowsPowershell\Modules')'"
}
Надеюсь, это поможет!
Ответ 4
Я подозреваю, что это новая функция, поскольку этот пост был создан - передать параметры в блок script, используя $Using: var. Тогда его простой mater для передачи параметров, если script уже находится на машине или в известном сетевом расположении относительно машины
Взяв в качестве основного примера:
icm -cn $Env:ComputerName {
C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 -one "uno" -two "dos" -Debug -Clear $Using:Clear
}