Pipe complete array-objects instead of array items one at a time?
arrays, object, pipeline, powershell, syntax
Solution
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.
Problem
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 pipeline¹. 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 - 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.