Как передать именованные параметры с помощью Invoke-Command? - PullRequest
70 голосов
/ 19 ноября 2010

У меня есть скрипт, который я могу запустить удаленно через Invoke-Command

Invoke-Command -ComputerName (Get-Content C:\Scripts\Servers.txt) `
               -FilePath C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1

Пока я использую параметры по умолчанию, он работает нормально.Однако в сценарии есть 2 именованных параметра [switch] (-Debug и -Clear)

Как передать передаваемые параметры через Invoke-Command?Я пробовал -ArgumentList, но получаю ошибки, поэтому у меня должен быть неправильный синтаксис или что-то в этом роде.Любая помощь с благодарностью.

Ответы [ 4 ]

90 голосов
/ 19 ноября 2010

-ArgumentList основан на использовании с scriptblock командами, такими как:

Invoke-Command -Cn (gc Servers.txt) {param($Debug=$False, $Clear=$False) C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 } -ArgumentList $False,$True

Когда вы вызываете его с -File, он по-прежнему передает параметры, как тупой массив. Я отправил запрос функции , чтобы добавить это к команде (пожалуйста, проголосуйте).

Итак, у вас есть два варианта:

Если у вас есть скрипт, который выглядел следующим образом, в сетевом расположении, доступном с удаленного компьютера (обратите внимание, что подразумевается -Debug, потому что когда я использую атрибут Parameter, сценарий неявно получает 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)

Другой вариант

Если скрипт находится на вашем локальном компьютере, и вы не хотите изменять параметры, чтобы они были позиционными, или вы хотите указать параметры, которые являются общими параметрами (поэтому вы не можете их контролировать), вы захотите получить содержимое этого скрипта и вставьте его в scriptblock :

$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 или переменная окружения $Env:Script с именем сценария, вы можете выполнить ее с помощью оператора вызова (&): &$Script или &$Env:Script

Если это переменная среды, которая уже определена на удаленном компьютере, это все, что нужно сделать. Если это локальная переменная, вам придется передать ее в удаленный блок скрипта:

Invoke-Command -cn $Env:ComputerName { 
    param([String]$Script, [bool]$Clear)
    &$Script "uno" "dos" -Debug -Clear:$Clear
} -ArgumentList $ScriptPath, $(Test-Path $Profile)
5 голосов
/ 23 мая 2016

Мое решение для этого было написать блок скриптов динамически с [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] (или файл сценария) таким образом, нет реальной проблемы сзаклинание "invoke-command".

(Это не должно быть так сложно. :))

4 голосов
/ 20 апреля 2017

Я подозреваю, что это новая функция с момента создания этого поста - передача параметров в блок скрипта с помощью $ Using: var.Затем просто передать параметры при условии, что скрипт уже находится на машине или в известном сетевом расположении относительно машины

Если взять основной пример, то это будет:

icm -cn $Env:ComputerName { 
    C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 -one "uno" -two "dos" -Debug -Clear $Using:Clear
}
3 голосов
/ 27 октября 2016

Мне нужно было что-то для вызова скриптов с именованными параметрами.У нас есть политика не использовать порядковое позиционирование параметров и требовать имя параметра.

Мой подход аналогичен приведенному выше, но получает содержимое файла сценария, который вы хотите вызвать, и отправляет блок параметров, содержащийпараметры и значения.

Одним из преимуществ этого является то, что вы можете при желании выбрать, какие параметры отправлять в файл сценария с учетом необязательных параметров со значениями по умолчанию.

Предполагая, что существуетскрипт с именем «MyScript.ps1» во временном пути, который имеет следующий блок параметров:

[CmdletBinding(PositionalBinding = $False)]
param
(
    [Parameter(Mandatory = $True)] [String] $MyNamedParameter1,
    [Parameter(Mandatory = $True)] [String] $MyNamedParameter2,
    [Parameter(Mandatory = $False)] [String] $MyNamedParameter3 = "some default value"
)

Вот как я бы назвал этот скрипт из другого скрипта:

$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

У меня естьиспользовал это во многих сценариях, и это работает очень хорошо.Одна вещь, которую вам иногда нужно делать, это заключать в кавычки блок назначения значений параметров.Это всегда тот случай, когда в значении есть пробелы.

Например, этот блок параметров используется для вызова скрипта, который копирует различные модули в стандартное расположение, используемое PowerShell C:\Program Files\WindowsPowerShell\Modules, который содержит символ пробела.

$params = @{
        SourcePath      = "$WorkingDirectory\Modules"
        DestinationPath = "'$(Join-Path -Path $([System.Environment]::GetFolderPath('ProgramFiles')) -ChildPath 'WindowsPowershell\Modules')'"
    }

Надеюсь, это поможет!

...