Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
835 views
in Technique[技术] by (71.8m points)

powershell - Pipe complete array-objects instead of array items one at a time?

How do you send the output from one CmdLet to the next one in a pipeline as a complete array-object instead of the individual items in the array one at a time?

The problem - Generic description
As can be seen in help for about_pipelines (help pipeline) powershell sends objects one at the time down the pipeline1. So Get-Process -Name notepad | Stop-Process sends one process at the time down the pipe.

Lets say we have a 3rd party CmdLet (Do-SomeStuff) that can't be modified or changed in any way. Do-SomeStuff perform different if it is passed an array of strings or if it is passed a single string object.

Do-SomeStuff is just an example, it could be substituted for ForEach-Object, Select-Object, Write-Host (or any other CmdLet accepting pipeline input)

Do-SomeStuff will in this example process the individual items in the array one at the time.

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

If we want to send the complete array as one object to Do-SomeStuff one might try something like this

@($theArray) | Do-SomeStuff

But it does not produce the expected result since PowerShell "ignores" the new single-item-array.

So, how do you "force" $theArray to be passed down the pipe as a single array-object instead of the content items one at the time?


The problem - practical example
As shown below the output of Write-Host is different if passed an array or if it passed the individual items in the array one at the time.

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

How do you do to get $theArray | foreach{Write-Host $_} to produce the same output as Write-Host $theArray ?




FOOTNOTES

  1. Pipeline processing in Powershell

A normal array of strings

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


A normal array of strings piped to Foreach-Object

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

Each string in the array is processed one at the time by the ForEach-Object CmdLet.


An array of arrays, where the "inner" arrays are arrays of strings.

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

Each array in the array is processed one at the time by the ForEach-Object CmdLet, and the content of each sub-array from the input is handled as one object even though it is an array.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Short answer: use unary array operator ,:

,$theArray | foreach{Write-Host $_}

Long answer: there is one thing you should understand about @() operator: it always interpret its content as statement, even if content is just an expression. Consider this code:

$a='A','B','C'
$b=@($a;)
$c=@($b;)

I add explicit end of statement mark ; here, although PowerShell allows to omit it. $a is array of three elements. What result of $a; statement? $a is a collection, so collection should be enumerated and each individual item should be passed by pipeline. So result of $a; statement is three elements written to pipeline. @($a;) see that three elements, but not the original array, and create array from them, so $b is array of three elements. Same way $c is array of same three elements. So when you write @($collection) you create array, that copy elements of $collection, instead of array of single element.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...