如何使用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)

如何通过调用命令传递已切换的参数?我已经尝试过-ArgumentList,但是出现错误,因此我必须使用错误的语法或其他内容。任何帮助是极大的赞赏。

Answers:


97

-ArgumentList基于与脚本块命令一起使用,例如:

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)

另一种选择

如果脚本在您的本地计算机上,并且您不想将参数更改为位置参数,或者您想要指定作为通用参数的参数(因此您无法控制它们),则需要获取以下内容:该脚本并将其嵌入到您的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

后记:

如果您确实需要为脚本名称传递一个变量,那么您将执行的操作将取决于该变量是在本地还是远程定义的。通常,如果您有一个带有脚本名称的变量$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一起使用。调用命令[-FilePath] <string> [[-Session] <PSSession []>] [-AsJob] [-HideComputerName] [-JobName <string>] [-T hrottleLimit <int>] [-ArgumentList <Object [ ]>] [-InputObject <psobject>] [<CommonParameters>]
ravikanth 2010年

1
是的ravikanth,但是ArgumentList似乎是通过溅到脚本上的,所以您不能指定命名参数吗?
Jaykul 2010年

8
可惜的是,StackOverflow无法理解PowerShell脚本...脚本名称的语法高亮显示有很多不足之处。
Jaykul 2010年

好的,看起来不错...必须尝试一下。但是,我有一个后续问题:如果使用icm -FilePath,它将把脚本复制到远程服务器,然后执行。如果我使用icm -Scriptblock,它似乎不会首先复制脚本-似乎假定脚本已在脚本块中指定的路径中的远程服务器上已经存在。这也是您的经验吗?
肖恩2010年

2
什么都没有真正复制到远程计算机。您指定的脚本将转换为ScriptBlock,然后将ScriptBlock传递给远程计算机,并且您对-ScriptBlock的理解是正确的
ravikanth 2010年

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”命令没有真正的问题。

(不应该那么难。:))


我同意。这是我唯一可以使用参数的解决方案,因为不需要传递参数。而且不能直接传播$ error。其他示例:使用[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
}

不知道它是否是新的,但是$ Using正是我想要的。感谢那!
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

我已经在很多情况下使用了它,并且效果很好。您偶尔需要做的一件事是在参数值分配块周围加上引号。当值中有空格时,总是这样。

例如,该参数块用于调用将各种模块复制到PowerShell使用的标准位置的脚本,该位置C:\Program Files\WindowsPowerShell\Modules包含空格字符。

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

希望这可以帮助!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.