Як передати іменовані параметри за допомогою Invoke-Command?


77

У мене є сценарій, який я можу запустити віддалено за допомогою Invoke-Command

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

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

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

Відповіді:


97

-ArgumentListбазується на використанні команд scriptblock , таких як:

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

Коли ви викликаєте його за допомогою a, -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)

Інший варіант

Якщо сценарій знаходиться на вашій локальній машині, і ви не хочете змінювати параметри на позиційні, або ви хочете вказати параметри, які є загальними параметрами (тому ви не можете ними керувати), ви захочете отримати вміст цей скрипт та вбудуйте його у свій блок скриптів :

$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)
    & $ScriptPath "uno" "dos" -Debug -Clear:$Clear
} -ArgumentList $ScriptPath, (Test-Path $Profile)

ArgumentList також доступний із -FilePath. Invoke-Command [-FilePath] <string> [[-Session] <PSSession []>] [-AsJob] [-HideComputerName] [-JobName <string>] [-T hrottleLimit <int>] [-ArgumentList <Object [ ]>] [-InputObject <psobject>] [<CommonParameters>]
ravikanth

1
Так, ravikanth, але, схоже, ArgumentList відбувається шляхом виведення на скрипт, тож ви не можете вказати іменовані параметри?
Jaykul

8
Шкода, що StackOverflow не розуміє сценарій PowerShell ... виділення синтаксису імен сценаріїв залишає бажати кращого.
Jaykul

Добре, це виглядає добре ... доведеться спробувати це. Однак у мене є наступне запитання: якщо я використовую icm -FilePath, він скопіює сценарій на віддалений сервер, а потім виконає. Якщо я використовую icm -Scriptblock, видається, що він не скопіює сценарій першим - здається, передбачається, що сценарій вже існує на віддаленому сервері у шляху, вказаному в блоці сценаріїв. Це теж ваш досвід?
Шон

2
На віддаленій машині насправді нічого не копіюється. Вказаний вами сценарій перетворюється на ScriptBlock, а потім ScriptBlock передається на віддалену машину, і ваше розуміння -ScriptBlock правильне
ravikanth

6

Моїм рішенням було написати блок сценарію динамічно за допомогою [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" немає.

(Це не повинно бути так важко. :))


Я згоден. Це єдине рішення, з яким я міг би працювати з параметрами просто тому, що немає необхідності передавати параметри. І не вдалося безпосередньо розповсюдити помилку $. Інший приклад: за допомогою [scriptblock] :: Create ("New-Item -Path '$ BackupPath' -ItemType Directory -Force"), я повинен змусити If ($ result.Exists) у абонента перевірити, чи не пішло щось не так. PSVersion = 5.1
реверпі

5

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

Беручи основний приклад, це буде:

icm -cn $Env:ComputerName { 
    C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 -one "uno" -two "dos" -Debug -Clear $Using:Clear
}

Не знаю, новий він чи ні, але $ Use було саме тим, що я шукав. Дякую за це!
JesusIsMyDriver.dll

3

Мені потрібно було щось викликати сценарії з названими параметрами. У нас є політика не використовувати порядкове позиціонування параметрів і вимагати імені параметра.

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

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

Припускаючи, що у тимчасовому шляху є сценарій під назвою "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

Я використовував це в багатьох сценаріях, і це працює дуже добре. Одне, що вам іноді потрібно робити, - це ставити лапки навколо блоку присвоєння значення параметра. Це завжди так, коли у значенні є пробіли.

Наприклад, цей блок param використовується для виклику сценарію, який копіює різні модулі у стандартне розташування, що використовується PowerShell, C:\Program Files\WindowsPowerShell\Modulesяке містить пробіл.

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

Сподіваюся, це допомагає!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.