I'm running a series of cmdlets in a pipeline, setting a -PipeLineVariable for each, but I'm getting unexpected results due to the use of a Sort-Object cmdlet higher in the pipeline.
For example (note this is not my full code, just a test to work through this particular problem).
get-vmhost -PipelineVariable VMHost | sort Name | select -first 2 | % {write-host $vmhost}
When I do this, the output for $VMhost is the same and it's the last VMHost in the pipeline.
I understand this to be a result of some cmdlets such as Sort-Object as having to aggregate all input to process and then output breaking the stream. I sort of understand this.
My larger question is, what is my alternative here? Without the Sort, my Select -First cmd returns random hosts, not the ones I'm looking hoping to see.
I know if I wasn't using Select-Object -First #, it would return all results and I could sort after at the very end of the pipeline, but this piqued my interest as there would be times (like this) where I want to use Select -First and want it to work on an expected result set of items that are first sorted so I really want to understand this.
CodePudding user response:
You simply need to use the common -PipelineVariable
parameter with the Sort-Object
cmdlet instead of Get-VMHost
in order to have the pipeline variable reflect the sorted object sequence.
A simplified example:
3, 6, 2, 1 | Sort-Object -PipelineVariable value | Select-Object -First 2 |
ForEach-Object { $value }
Output:
1
2
CodePudding user response:
The code in your question is behaving as expected / designed (see below).
I'm not sure what you want achieve, but probably you want the following:
$myVMs = get-vmhost | sort Name | select -first 2
$myVMs | ForEach-Object { write-host $_ }
or, in a single pipeline:
get-vmhost | sort Name | select -first 2 | ForEach-Object { write-host $_ }
Reference: about_CommonParameters
PipelineVariable allows access to the most recent value passed into the next pipeline segment by the command that uses this parameter. Any command in the pipeline can access the value using the named PipelineVariable. The value is assigned to the variable when it is passed into the next pipeline segment. This makes the PipelineVariable easier to use than a specific temporary variable, which might need to be assigned in multiple locations.
Unlike
$_
or$PSItem
, using a PipelineVariable allows any pipeline command to access pipeline values passed (and saved) by commands other than the immediately preceding command. Pipeline commands can access the last value piped from while processing the next item passing through the pipeline. This allows a command to feed back its output to a previous command (or itself).
Caution
The PipelineVariable is scoped to the pipeline in which it is invoked. Variables outside the pipeline, which use same name, are removed before the pipeline is executed. The PipelineVariable goes out of scope when the pipeline terminates. If multiple commands within the pipeline specify the same PipelineVariable then there is only one shared variable. That variable is updated with the most recent piped output from the command that specifies the variable.
Some blocking commands collect all the pipeline items before producing any output, for example Sort-Object or Select-Object -Last. Any PipelineVariable assigned in a command before such a blocking command always contains the final piped item from the preceding command when used in a command after the blocking command.