【问题标题】:PowerShell: What exactly is $input inside Start-Job?PowerShell:Start-Job 中的 $input 到底是什么?
【发布时间】:2014-02-01 17:13:56
【问题描述】:

考虑以下代码。我只是通过使用-InputObject 参数将一个32 位有符号整数数组[Int32[]] 传递到Start-Job cmdlet。

$Job = Start-Job -ScriptBlock { $input.GetType().FullName; } -InputObject @(1,2,3);
Wait-Job -Job $Job;
Receive-Job -Keep $Job;

这段代码的结果是:

System.Management.Automation.Runspaces.PipelineReader`1+<GetReadEnumerator>d__0[[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]

查看PipelineReader .NET class 的文档,它有一个ReadToEnd() 方法。因此,下面的代码应该可以工作:

$Job = Start-Job -ScriptBlock { $input.ReadToEnd(); } -InputObject @(1,2,3);
Wait-Job -Job $Job;
Receive-Job -Keep $Job;

但是,我收到一条错误消息:

方法调用失败,因为 [System.Int32] 不包含名为“ReadToEnd”的方法。 + CategoryInfo : InvalidOperation: (:) [], RuntimeException + FullyQualifiedErrorId : MethodNotFound + PSComputerName : 本地主机

那么我想,我将只使用PSBase 属性来获取“真实”对象。

$Job = Start-Job -ScriptBlock { $input.psbase.ReadToEnd(); } -InputObject @(1,2,3);
Wait-Job -Job $Job;
Receive-Job -Keep $Job;

然后我收到类似的错误消息:

方法调用失败,因为 [System.Management.Automation.PSInternalMemberSet] 不包含名为“ReadToEnd”的方法。 + CategoryInfo : InvalidOperation: (ReadToEnd:String) [], RuntimeException + FullyQualifiedErrorId : MethodNotFound + PSComputerName : 本地主机

我注意到围绕这种混乱有一个Microsoft Connect bug filed,但它让我更加困惑。显然PipelineReader 类有一个名称混淆的属性&lt;&gt;4__this,它有一个Read() 方法,使用Get-Member 是看不到的。

底线:当通过Start-Job cmdlet 上的-InputObject 参数提交输入时,有谁知道如何简单地“解包”$input 自动变量的内容,所以我可以单独处理这些对象吗?

这个脚本应该只返回1,而不是1, 2, 3

$Job = Start-Job -ScriptBlock { $input[0]; } -InputObject @(1,2,3);
Wait-Job -Job $Job;
Receive-Job -Keep $Job;

【问题讨论】:

  • $Job = Start-Job -ScriptBlock { $($input)[0]; } -InputObject @(1,2,3) 适合你吗?
  • 是的,但您能进一步解释一下$input 是什么吗?这就是我最终想要理解的。
  • 我想我有一个更好的例子。 about_Automatic_Variables 在枚举器中表示$input,您不能像数组一样对其进行索引。试试这个:$Job = Start-Job -ScriptBlock { $input.getEnumerator()[0] } -InputObject @(1,2,3)

标签: powershell windows-8.1 powershell-4.0


【解决方案1】:

大概$input 是一个枚举器,就像在标准管道中一样。 为了处理项目,我们应该使用 process 块和 自动变量$_ 或在end 的另一个管道中传递$input 块(如果未指定,则为隐式)。

# process each item separately
$Job = Start-Job -ScriptBlock {process{$_}} -InputObject @(1,2,3);
Wait-Job -Job $Job;
Receive-Job -Keep $Job;

# process the whole $input
$Job = Start-Job -ScriptBlock {$input | %{$_}} -InputObject @(1,2,3);
Wait-Job -Job $Job;
Receive-Job -Keep $Job;

# compare with script blocks in standard pipelines

# each item
@(1,2,3) | . {process{$_}}

# whole input
@(1,2,3) | . {$input | %{$_}}

也许还有其他方法可以枚举$input 的项目,但它们是 据我所知,在实践中并不经常使用。

【讨论】:

    【解决方案2】:

    这似乎有效:

    $Job = Start-Job -ScriptBlock { $input.movenext();$input.current[0] } -InputObject @(1,2,3);
    Wait-Job -Job $Job;
    Receive-Job -Keep $Job;
    

    【讨论】:

      【解决方案3】:
      $Input
         Contains an enumerator that enumerates all input that is passed to a
         function. The $input variable is available only to functions and script
         blocks (which are unnamed functions).  In the Process block of a
         function, the $input variable enumerates the object that is currently
         in the pipeline. When the Process block  completes, there are no objects
         left in the pipeline, so the $input variable enumerates an empty
         collection. If the function does not have a Process block, then in the
         End block, the $input variable enumerates the collection of all input to
         the function.
      

      来源:about_Automatic_Variables

      总结一下:$input 是一个自动变量,它以枚举器的形式包含整个管道,而 $_ 是管道中的“当前对象”。

      这是一个如何使用它的示例。

      function test {
          #$input is an enumerator that you should use $input | foreach-object { } to access the objects.
      
          #To to get all items you could e.g. convert the enumerator to an array.
          $arr = @($input)
      
          #If you need to use the $input enumerator for something else, you need to call .Reset() first as the enumerator has reached the end.
          $input.Reset()
      
          #Print some values from the data
          $arr.count
          $arr[0]
      }
      
      PS> "hello", "world" | test
      
      
      2
      hello
      

      更新:这是证明这也适用于您Start-Job 场景的证据。示例注释说明。

      $Job = Start-Job -ScriptBlock { 
          #Read the complete pipeline to an array
          $data= @($input)
          "`$data is an $($data.GetType().Name) with $($data.count) objects"
      
          #Unlike in a pipeline, where the `Start-Job` command would be called once per object like `Start-Job ...... -InputObject $_`, 
          #you're inputing a single `object[]` object the the pipeline. So you only have one item in your $input pipeline.
      
          #Get our inputobject (our array)
          $arr= $data[0]
          "`$arr is an $($arr.GetType().Name)"
      
          #Use array
          "$($arr[0]) is less than $($arr[1]) which is less than $($arr[2])"
      } -InputObject @(1,2,3);
      
      Wait-Job -Job $Job;
      Receive-Job -Keep $Job;
      
      Id     Name            PSJobTypeName   State         HasMoreData     Location             Command                  
      --     ----            -------------   -----         -----------     --------             -------                  
      54     Job54           BackgroundJob   Completed     True            localhost             ...                     
      $data is an Object[] with 1 objects
      $arr is an ArrayList
      1 is less then 2 which is less then 3
      

      并证明我关于管道的“理论”。这是流水线版本:

      $Job = 1, 2, 3 | Start-Job -ScriptBlock { 
          #Read the complete pipeline to an array
          $data= @($input)
          "`$data is an $($data.GetType().Name) with $($data.count) objects"
      
          #Now the command is run per object, so the $input enumerator contained our 3 seperate Int32 values.
      
          #Get a single object in the pipeline
          $OneOfTheValues= $data[0]
          "`$OneOfTheValues is an $($OneOfTheValues.GetType().Name)"
      
          #Use data
          "$($data[0]) is less than $($data[1]) which is less than $($data[2])"
      }
      
      Wait-Job -Job $Job;
      Receive-Job -Keep $Job;
      
      Id     Name            PSJobTypeName   State         HasMoreData     Location             Command                  
      --     ----            -------------   -----         -----------     --------             -------                  
      58     Job58           BackgroundJob   Completed     True            localhost             ...                     
      $data is an Object[] with 3 objects
      $OneOfTheValues is an Int32
      1 is less then 2 which is less then 3
      

      所以我坚持我原来的答案。行为是相同的,您只是以不同的方式使用管道/cmdlet。 :)

      【讨论】:

      • Start-Job 不会这样。请参阅我的示例。
      猜你喜欢
      • 2014-11-03
      • 2013-05-29
      • 2012-06-11
      • 1970-01-01
      • 1970-01-01
      • 2013-02-06
      • 2013-05-05
      • 2010-11-19
      • 2012-06-16
      相关资源
      最近更新 更多