【问题标题】:How do I pass named parameters with Invoke-Command?如何使用 Invoke-Command 传递命名参数?
【发布时间】:2011-05-12 15:55:23
【问题描述】:

我有一个可以通过 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 但我遇到了错误,所以我必须有语法错误或其他东西。非常感谢任何帮助。

【问题讨论】:

    标签: powershell invoke-command


    【解决方案1】:

    这是一个不幸的情况。位置参数起作用。

    # 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
    

    或者你可以硬编码一个默认值。

    # 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.
    

    或者复制那里的脚本:

    $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.
    

    【讨论】:

      【解决方案2】:

      -ArgumentList 基于与 scriptblock 命令一起使用,例如:

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

      当您使用-File 调用它时,它仍然会像哑弹数组一样传递参数。我已经提交了 feature request 以将其添加到命令中(请投票)。

      所以,你有两个选择:

      如果您有一个看起来像这样的脚本,位于可从远程计算机访问的网络位置(请注意,-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
      

      在那一个中​​,我在 scriptblock 中复制了我关心的所有参数,以便我可以传递值。如果我可以对它们进行硬编码(这是我实际所做的),则无需这样做并使用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

      如果它是已经在远程计算机上定义的环境变量,那就是它的全部。如果它是 local 变量,那么您必须将它传递给远程脚本块:

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

      【讨论】:

      • ArgumentList 也可与 -FilePath 一起使用。调用命令 [-FilePath] [[-Session] ] [-AsJob] [-HideComputerName] [-JobName ] [-T hrottleLimit ] [-ArgumentList ] [-InputObject ] []
      • 是的 ravikanth,但似乎 ArgumentList 是通过 splatting 到脚本的,所以你不能指定 named 参数?
      • 很遗憾 StackOverflow 不理解 PowerShell 脚本......脚本名称的语法突出显示还有很多不足之处。
      • 好的,看起来不错...必须尝试一下。但是,我有一个后续问题:如果我使用 icm -FilePath 它会将脚本复制到远程服务器然后执行。如果我使用 icm -Scriptblock 它似乎不会首先复制脚本——它似乎假设脚本已经存在于远程服务器上脚本块中指定的路径中。这也是你的经历吗?
      • 没有任何东西真正被复制到远程机器上。您指定的脚本被转换为 ScriptBlock,然后 ScriptBlock 被传递到远程机器,您对 -ScriptBlock 的理解是正确的
      【解决方案3】:

      自从创建这篇文章以来,我怀疑它是一个新功能 - 使用 $Using:var 将参数传递给脚本块。如果脚本已经在机器上或相对于机器的已知网络位置,那么它是一个简单的传递参数的方法

      举个主要的例子:

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

      【讨论】:

      • 不知道它是否是新的,但 $Using 正是我想要的。谢谢!
      【解决方案4】:

      我需要一些东西来调用带有命名参数的脚本。我们的政策是不使用参数的顺序定位并要求参数名称。

      我的方法与上面的方法类似,但获取您要调用的脚本文件的内容并发送一个包含参数和值的参数块。

      这样做的好处之一是您可以选择将哪些参数发送到脚本文件,从而允许使用默认值的非强制性参数。

      假设临时路径中有一个名为“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')'"
          }
      

      希望这会有所帮助!

      【讨论】:

        【解决方案5】:

        我对此的解决方案是使用 [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
        猜你喜欢
        • 1970-01-01
        • 2016-09-24
        • 2023-04-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多