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

NoO*_*ial 26 arrays syntax powershell pipeline object

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

问题 - 通用描述
about_pipelines(help pipeline)的帮助中可以看到,powershell在管道¹下发送一个对象.因此Get-Process -Name notepad | Stop-Process,在管道下方发送一个进程.

假设我们有第三方CmdLet(Do-SomeStuff)无法以任何方式修改或更改.如果传递一个字符串数组或传递一个字符串对象,则Do-SomeStuff执行不同的操作.

DO-SomeStuff仅仅是一个例子,它可以被取代ForEach-Object,Select-Object,Write-Host(或任何其它小命令接受管道输入)

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

$theArray = @("A", "B", "C")
$theArray | Do-SomeStuff
Run Code Online (Sandbox Code Playgroud)

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

@($theArray) | Do-SomeStuff
Run Code Online (Sandbox Code Playgroud)

但由于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
Run Code Online (Sandbox Code Playgroud)

你怎么做到$theArray | foreach{Write-Host $_}产生相同的输出Write-Host $theArray




脚注

  1. Powershell中的管道处理

一个正常的字符串数组

PS C:\> @("A", "B", "C").GetType().FullName
System.Object[]
Run Code Online (Sandbox Code Playgroud)


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

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

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


一组数组,其中"内部"数组是字符串数组.

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

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

use*_*407 31

简答:使用一元数组运算符,:

,$theArray | foreach{Write-Host $_}
Run Code Online (Sandbox Code Playgroud)

答案长:关于@()运算符,你应该理解一件事:它总是把它的内容解释为语句,即使内容只是一个表达式.考虑以下代码:

$a='A','B','C'
$b=@($a;)
$c=@($b;)
Run Code Online (Sandbox Code Playgroud)

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

  • 谢谢,现在,当你告诉我要查找什么时,请在文档中[TechNet:about_Operators](https://technet.microsoft.com/en-us/library/hh847732.aspx)"_,逗号运算符二元运算符,逗号创建一个数组.作为一元运算符,逗号创建一个包含一个成员的数组.将逗号放在成员之前._` $ myArray = 1,2,3 $ SingleArray =,1`" (2认同)
  • @NoOneSpecial `$array=,@()` (2认同)

Ro *_* Mi 5

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

如果您需要评估阵列中多个项目的状态,这将很有用。

使用以下功能

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 
Run Code Online (Sandbox Code Playgroud)

考虑以下示例:

仅将数组包装在括号中并不能保证该函数将在一个过程调用中处理值的数组。在此示例中,我们看到了数组中每个元素的随机数变化。

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

简单地添加前导逗号,也不保证该函数将在一个流程调用中处理值的数组。在此示例中,我们看到了数组中每个元素的随机数变化。

PS C:\> , 1,2,3,4,5 | funTest
1000 - 1
84 - 2
813 - 3
156 - 4
928 - 5
Run Code Online (Sandbox Code Playgroud)

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

PS C:\> , @( 1,2,3,4,5) | funTest
883 - 1
883 - 2
883 - 3
883 - 4
883 - 5
Run Code Online (Sandbox Code Playgroud)


小智 5

如果您不介意您的流程是一个函数,那么有一个老式的解决方案。

示例:您希望将数组复制到剪贴板,以便您可以在没有任何 PSRemoting 连接的另一个系统上再次构建它。因此,您希望将包含“A”、“B”和“C”的数组转换为字符串: @("A","B","C") ...而不是文字数组。

所以你构建了这个(由于其他原因这不是最佳的,但保持主题):

# Serialize-List

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

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

    $output = $output.Substring(0, $output.Length - 1)
    $output += ")"
    $output
Run Code Online (Sandbox Code Playgroud)

当您直接指定数组作为参数时它会起作用:

Serialize-List $list
@("A","B","C")
Run Code Online (Sandbox Code Playgroud)

...但当你通过管道传递它时就不那么重要了:

$list | Serialize-List
@("C")
Run Code Online (Sandbox Code Playgroud)

但是用 begin、process 和 end 块重构你的函数:

# 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
}
Run Code Online (Sandbox Code Playgroud)

...并且您可以通过两种方式获得所需的输出。

Serialize-List $list
@("A","B","C")

$list | Serialize-List
@("A","B","C")
Run Code Online (Sandbox Code Playgroud)