简体   繁体   English

如何使用 Invoke-Command 传递命名参数?

[英]How do I pass named parameters with Invoke-Command?

I have a script that I can run remotely via Invoke-Command我有一个可以通过 Invoke-Command 远程运行的脚本

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

As long as I use default parameters, it works fine.只要我使用默认参数,它就可以正常工作。 However, the script has 2 named [switch] parameters (-Debug and -Clear)但是,该脚本有 2 个名为 [switch] 的参数(-Debug 和 -Clear)

How can I pass the switched parameters via the Invoke-Command?如何通过 Invoke-Command 传递切换的参数? I've tried the -ArgumentList but I'm getting errors so I must have the syntax wrong or something.我已经尝试过 -ArgumentList 但我遇到了错误,所以我必须有语法错误或其他东西。 Any help is greatly appreciated.任何帮助是极大的赞赏。

-ArgumentList is based on use with scriptblock commands, like: -ArgumentList基于与scriptblock命令一起使用,例如:

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

When you call it with a -File it still passes the parameters like a dumb splatted array.当您使用-File调用它时,它仍会像哑弹数组一样传递参数。 I've submitted a feature request to have that added to the command (please vote that up).我已经提交了一个功能请求,将其添加到命令中(请投票)。

So, you have two options:所以,你有两个选择:

If you have a script that looked like this, in a network location accessible from the remote machine (note that -Debug is implied because when I use the Parameter attribute, the script gets CmdletBinding implicitly, and thus, all of the common parameters):如果您有一个看起来像这样的脚本,位于可从远程计算机访问的网络位置(请注意-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."

Without getting hung up on the meaning of $Clear ... if you wanted to invoke that you could use either of the following Invoke-Command syntaxes:不必纠结$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

In that one, I'm duplicating ALL the parameters I care about in the scriptblock so I can pass values.在那一个中​​,我在脚本块中复制了我关心的所有参数,以便我可以传递值。 If I can hard-code them (which is what I actually did), there's no need to do that and use PSBoundParameters , I can just pass the ones I need to.如果我可以对它们进行硬编码(这是我实际所做的),则无需这样做并使用PSBoundParameters ,我可以传递我需要的那些。 In the second example below I'm going to pass the $Clear one, just to demonstrate how to pass switch parameters:在下面的第二个示例中,我将传递 $Clear 一个,只是为了演示如何传递开关参数:

icm -cn $Env:ComputerName { 
    param([bool]$Clear)
    C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 "uno" "dos" -Debug -Clear:$Clear
} -ArgumentList $(Test-Path $Profile)

The other option另一种选择

If the script is on your local machine, and you don't want to change the parameters to be positional, or you want to specify parameters that are common parameters (so you can't control them) you will want to get the content of that script and embed it in your 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:后记:

If you really need to pass in a variable for the script name, what you'd do will depend on whether the variable is defined locally or remotely.如果您确实需要为脚本名称传递一个变量,那么您要做的将取决于该变量是在本地还是远程定义的。 In general, if you have a variable $Script or an environment variable $Env:Script with the name of a script, you can execute it with the call operator (&): &$Script or &$Env:Script一般来说,如果你有一个带有脚本名称的变量$Script或环境变量$Env:Script ,你可以用调用运算符 (&) 执行它: &$Script&$Env:Script

If it's an environment variable that's already defined on the remote computer, that's all there is to it.如果它是已经在远程计算机上定义的环境变量,那就是它的全部内容。 If it's a local variable, then you'll have to pass it to the remote script block:如果它是一个局部变量,那么您必须将它传递给远程脚本块:

Invoke-Command -cn $Env:ComputerName { 
    param([String]$Script, [bool]$Clear)
    & $ScriptPath "uno" "dos" -Debug -Clear:$Clear
} -ArgumentList $ScriptPath, (Test-Path $Profile)

My solution to this was to write the script block dynamically with [scriptblock]:Create :我对此的解决方案是使用[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

Passing arguments was very frustrating, trying various methods, eg,传递参数非常令人沮丧,尝试各种方法,例如,
-arguments , $using:p1 , etc. and this just worked as desired with no problems. -arguments$using:p1等,这只是按预期工作,没有问题。

Since I control the contents and variable expansion of the string which creates the [scriptblock] (or script file) this way, there is no real issue with the "invoke-command" incantation.由于我以这种方式控制创建[scriptblock] (或脚本文件)的字符串的内容和变量扩展,因此“invoke-command”咒语没有真正的问题。

(It shouldn't be that hard. :) ) (这不应该那么难。:))

I suspect its a new feature since this post was created - pass parameters to the script block using $Using:var.我怀疑它是自创建此帖子以来的一项新功能 - 使用 $Using:var 将参数传递给脚本块。 Then its a simple mater to pass parameters provided the script is already on the machine or in a known network location relative to the machine如果脚本已经在机器上或相对于机器的已知网络位置,那么它是一个简单的传递参数的方法

Taking the main example it would be:以主要示例为例:

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

I needed something to call scripts with named parameters.我需要一些东西来调用带有命名参数的脚本。 We have a policy of not using ordinal positioning of parameters and requiring the parameter name.我们的政策是不使用参数的顺序定位并要求参数名称。

My approach is similar to the ones above but gets the content of the script file that you want to call and sends a parameter block containing the parameters and values.我的方法与上面的方法类似,但获取您要调用的脚本文件的内容并发送一个包含参数和值的参数块。

One of the advantages of this is that you can optionally choose which parameters to send to the script file allowing for non-mandatory parameters with defaults.这样做的优点之一是您可以选择将哪些参数发送到脚本文件,从而允许使用默认值的非强制性参数。

Assuming there is a script called "MyScript.ps1" in the temporary path that has the following parameter block:假设在具有以下参数块的临时路径中有一个名为“MyScript.ps1”的脚本:

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

This is how I would call this script from another 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

I have used this in lots of scenarios and it works really well.我在很多场景中都使用过它,而且效果非常好。 One thing that you occasionally need to do is put quotes around the parameter value assignment block.您偶尔需要做的一件事是在参数值分配块周围加上引号。 This is always the case when there are spaces in the value.当值中有空格时,总是会出现这种情况。

eg This param block is used to call a script that copies various modules into the standard location used by PowerShell C:\Program Files\WindowsPowerShell\Modules which contains a space character.例如,此参数块用于调用将各种模块复制到 PowerShell C:\Program Files\WindowsPowerShell\Modules使用的标准位置的脚本,其中包含空格字符。

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

Hope this helps!希望这可以帮助!

This is an unfortunate situation.这是一个不幸的情况。 Positional parameters work.位置参数起作用。

# test.ps1
param($myarg1, $myarg2, $myarg3)

"myarg1 $myarg1"
"myarg2 $myarg2"
"myarg3 $myarg3"
# elevated prompt
invoke-command localhost test.ps1 -args 1,$null,3

myarg1 1
myarg2
myarg3 3

Or you can hardcode a default.或者您可以硬编码默认值。

# test2.ps1
param($myarg='foo2')

dir $myarg
invoke-command localhost test2.ps1

Cannot find path 'C:\Users\js\Documents\foo2' because it does not exist.

Or copy the script there:或者在那里复制脚本:

$s = New-PSSession localhost
copy-item test2.ps1 $home\documents -ToSession $s
icm $s { .\test2.ps1 -myarg foo3 }

Cannot find path 'C:\Users\js\Documents\foo3' because it does not exist.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM