管道完整的数组对象而不是一次一个数组项?

时间:2015-04-30 16:35:22

标签: arrays powershell object syntax pipeline

如何将一个CmdLet的输出作为完整的数组对象发送到管道中的下一个,而不是一次一个地发送一个CmdLet中的单个项目?

问题 - 通用描述
about_pipelineshelp pipeline)的帮助中可以看出,powershell在流水线下发送一个对象¹。因此,Get-Process -Name notepad | Stop-Process会在管道下发送一个进程。

假设我们有第三方CmdLet(Do-SomeStuff)无法以任何方式修改或更改。 Do-SomeStuff在传递一个字符串数组或传递一个字符串对象时执行不同。

Do-SomeStuff只是一个示例,它可以替代ForEach-ObjectSelect-ObjectWrite-Host(或任何其他CmdLet接受管道输入) < / p>

Do-SomeStuff将在此示例中处理数组中的各个项目。

$theArray = @("A", "B", "C")
$theArray | Do-SomeStuff

如果我们想将完整数组作为一个对象发送到Do-SomeStuff,可以尝试这样的事情

@($theArray) | Do-SomeStuff

但是由于PowerShell“忽略”了新的单项数组,因此它不会产生预期的结果。

那么,你如何“强迫”$theArray作为单个数组对象传递给管道而不是当时的内容项?


问题 - 实际例子
如下所示,Write-Host的输出在传递数组时是不同的,或者如果它一次传递一个数组中的单个项目。

PS C:\> $theArray = @("A", "B", "C")
PS C:\> Write-Host $theArray
A B C
PS C:\> $theArray | foreach{Write-Host $_}
A
B
C
PS C:\> @($theArray) | foreach{Write-Host $_}
A
B
C

如何让$theArray | foreach{Write-Host $_}生成与Write-Host $theArray相同的输出?



脚注

  1. Powershell中的管道处理
  2. 正常的字符串数组

    PS C:\> @("A", "B", "C").GetType().FullName
    System.Object[]
    


    传递给Foreach-Object的正常字符串数组

    PS C:\> @("A", "B", "C") | foreach{$_.GetType().FullName}
    System.String
    System.String
    System.String
    

    数组中的每个字符串由ForEach-Object CmdLet一次处理一次。


    一组数组,其中“内部”数组是字符串数组。

    PS C:\> @(@("A", "B", "C"), @("D", "E", "F"), @("G", "H", "I")) | foreach{$_.GetType().FullName}
    System.Object[]
    System.Object[]
    System.Object[]
    

    数组中的每个数组都由ForEach-Object CmdLet一次处理,并且输入中每个子数组的内容作为一个对象处理,即使它是一个数组。

4 个答案:

答案 0 :(得分:28)

简答:使用一元数组运算符.leftMenu, .rightMenu, .centerMenu { text-align: center; display: inline-block; border-bottom: solid .1em; display: -webkit-flex; } .leftMenu, .rightMenu { background-color: #454ed6; height: 2em; padding-top: 1em; width: 17.5%; -webkit-justify-content: space-around; } .leftMenu { float: left; } .rightMenu { float: right; } .centerMenu a { display: block; height: 2em; } .centerMenu { width: 65%; height: 6em; background-color: #86acbe; -webkit-flex-direction: column; -webkit-justify-content: space-around; }

,

答案很长:关于,$theArray | foreach{Write-Host $_} 运算符,您应该了解一件事:它始终将其内容解释为语句,即使内容只是表达式。请考虑以下代码:

@()

我在这里添加了显式结束语句标记$a='A','B','C' $b=@($a;) $c=@($b;) ,尽管PowerShell允许省略它。 ;是三个元素的数组。 $a陈述的结果是什么? $a;是一个集合,因此应该枚举集合,并且每个单独的项都应该通过管道传递。因此$a语句的结果是写入管道的三个元素。 $a;看到三个元素,但不是原始数组,并从中创建数组,因此@($a;)是三个元素的数组。同样的方式$b是相同三个元素的数组。因此,当您编写$c创建数组时,会复制@($collection)的元素,而不是单个元素的数组。

答案 1 :(得分:4)

逗号字符使数据成为数组。为了使管线将数组作为数组处理,而不是单独对每个数组元素进行操作,您可能还需要用括号包装数据。

如果您需要评估数组中多个项目的状态,这非常有用。

使用以下功能

function funTest {
    param (
        [parameter(Position=1, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
        [alias ("Target")]
        [array]$Targets 
        ) # end param
    begin {}
    process {
        $RandomSeed = $( 1..1000 | Get-Random )
        foreach ($Target in $Targets) {
            Write-Host "$RandomSeed - $Target"
            } # next target
        } # end process
    end {}
    } # end function 

请考虑以下示例:

将数组括在括号中并不能保证函数将在一个进程调用中处理值数组。在这个例子中,我们看到了数组中每个元素的随机数变化。

PS C:\> @(1,2,3,4,5) | funTest
153 - 1
87 - 2
96 - 3
96 - 4
986 - 5

只需添加前导逗号,也不保证函数将在一个进程调用中处理值数组。在这个例子中,我们看到了数组中每个元素的随机数变化。

PS C:\> , 1,2,3,4,5 | funTest
1000 - 1
84 - 2
813 - 3
156 - 4
928 - 5

使用括号中的前导逗号和值数组,我们可以看到随机数保持不变,因为正在利用函数的foreach命令。

PS C:\> , @( 1,2,3,4,5) | funTest
883 - 1
883 - 2
883 - 3
883 - 4
883 - 5

答案 2 :(得分:2)

如果你不介意你的过程是一个功能,那么这是一个老派的解决方案。

示例:您希望将数组复制到剪贴板的方式允许您在没有任何PSRemoting连接的情况下在另一个系统上再次构建它。所以你想要一个包含&#34; A&#34;,&#34; B&#34;和&#34; C&#34;转换为字符串: @(&#34; A&#34;&#34; B&#34;&#34; C&#34) ...而不是文字数组。

所以你建立了这个(由于其他原因这不是最优的,但留在主题上):

# Serialize-List

param 
(
    [Parameter(Mandatory, ValueFromPipeline)]
    [string[]]$list
)
    $output = "@(";

    foreach ($element in $list)
    {
        $output += "`"$element`","
    }

    $output = $output.Substring(0, $output.Length - 1)
    $output += ")"
    $output

,当您将数组直接指定为参数时,它可以正常工作:

Serialize-List $ list

@(&#34; A&#34;&#34; B&#34;&#34; C&#34)

...但是当你通过管道传递它时没那么多:

$ list |序列化-列表

@(&#34; C&#34)

但是使用begin,process和end blocks重构你的函数:

# Serialize-List

param 
(
    [Parameter(Mandatory, ValueFromPipeline)]
    [string[]]$list
)

begin
{
    $output = "@(";
}

process
{
    foreach ($element in $list)
    {
        $output += "`"$element`","
    }
}

end
{
    $output = $output.Substring(0, $output.Length - 1)
    $output += ")"
    $output
}

......你可以双向获得所需的输出。

Serialize-List $ list

@(&#34; A&#34;&#34; B&#34;&#34; C&#34)

$ list |序列化-列表

@(&#34; A&#34;&#34; B&#34;&#34; C&#34)

答案 3 :(得分:1)

$ar="1","2","3"

$ar | foreach { $_ }