PipelineTools.psm1
function Get-PipelineMethod { <# .SYNOPSIS Given two commands in a pipeline this command discovers if and how the pipeline will operate .DESCRIPTION PowerShell always tries to pipeline data between two commands using ByValue first, if this is not possbile then PowerShell tries ByPropertyName next. Given two commands in a pipeline this command will determine whether the pipeline will work and how it will pipe the information. It will determine whether the pipeline will use ByValue or ByPropertyName piping methods. For ByValue pipelines it will show the first and second commands along with the object type that is being sent across the pipeline from the output of the first command, and which parameter in the second command will accept that object type ByValue. For ByPropertyName the first and second commands will be listed along with each property from the object produced by the first command that coresponds to parameters in the second command, the condition that allows matches between properties to parameters is that they must be spelt the same and have the same object type. This program uses standard pipeline proof techniques such as: FirstCommand | Get-Member Get-Help -Full SecondCommmand It then compares the results to determine how/if the pipeline can pipe the data from the FirstCommand to the SecondCommand .PARAMETER FirstCommand This represents the first command in a pipeline .PARAMETER FirstCommand This represents the second command in a pipeline .NOTES Created By: Brent Denny On: 2 Aug 2022 Last Edited: By: Brent Denny On: 8 Aug 2022 Version History Version Changes made 0.9 Final edits to get the ByPropertyName result working, there is still an issue with the ByValue output if the first command outputs multiple objects, I have not resolved this problem yet 0.95 Fixed a problem that was declaring a real command as one that did not exist .EXAMPLE Get-PipelineMethod -FirstCommand Get-Service -SecondCommand Stop-Service If you are trying to determine how: Get-Service -Name Test | Stop-Service will pipeline the information. This command will determine how this will work and what information is pipelined from the first command to the second command #> [CmdletBinding()] Param ( [Parameter(Mandatory=$true)] [String]$FirstCommand , [Parameter(Mandatory=$true)] [String]$SecondCommand ) function Get-ObjectType { Param ( [string]$Command ) try { $CmdObjTypes = ((Invoke-Expression $Command -ErrorAction stop) | ForEach-Object {$_.gettype()}).Name | Select-Object -Unique return $CmdObjTypes } catch { Write-Warning "$FirstCommand - does not appear to be a valid command" break } } function Get-ObjectProperty { Param ( [string]$Command ) $CmdProperties = Invoke-Expression $Command | Get-Member -MemberType Properties $CmdPropTypes = $CmdProperties | Select-Object name,MemberType,@{ n='Type'; e={ if ($_.MemberType -eq 'AliasProperty') { $AliasTo = ($_.Definition -split '\s+')[-1] $MappedType = ($CmdProperties | Where-Object {$_.Name -eq $AliasTo}).Definition (($MappedType -split '\s')[0]) -replace '^.*\.','' -replace '\[\]','' } else {(($_.Definition -split '\s')[0]) -replace '^.*\.','' -replace '\[\]',''} } } return $CmdPropTypes } function Get-ObjectMember { Param ( [string]$Command, [ValidateSet('ByValue','ByPropertyName')] [string]$PipelineMethod ) $CmdHelp = try { $AllCommands = (Get-Command).Name if ($AllCommands -notcontains $Command) {throw "No such command"} Get-help -Full -Name $Command -ErrorAction stop } catch { Write-Warning "There does not seem to be a help topic the the command $SecondCommand`nThis command requires PowerShell help pages for the second command" break } $PipelineParams = foreach ($Parameter in $CmdHelp.parameters.parameter) { if ($Parameter.PipelineInput -match $PipelineMethod) { $ByPNProps = [ordered]@{ Name = $Parameter.Name Type = $Parameter.Type.Name -replace '^.*\.','' -replace '\[\]','' Method = $PipelineMethod } New-Object -TypeName psobject -Property $ByPNProps } } return $PipelineParams } function Show-ByVal { Param ( $FirstCmd, $SecondCmd, $FirstCmdType, $SecondCmdParamByVal ) Clear-Host Write-Host -ForegroundColor Blue "ByValue Pipeline" Write-Host -ForegroundColor Blue "----------------" Write-Host "The first command " -NoNewline Write-Host -ForegroundColor Red $FirstCmd -NoNewline Write-Host " produces an object of type " -NoNewline Write-Host -ForegroundColor Red $FirstCmdType Write-Host "The second command " -NoNewline Write-Host -ForegroundColor Red $SecondCmd -NoNewline Write-Host " accepts " -NoNewline Write-Host -ForegroundColor Red $FirstCmdType -NoNewline Write-Host " objects " Write-Host "being pipelined " -NoNewline Write-Host -ForegroundColor Blue "ByValue" -NoNewline Write-Host " via the parameter -" -NoNewline Write-Host -ForegroundColor Red $SecondCmdParamByVal[0].Name if ($SecondCmdParamByVal[0].Type -eq 'PSObject') { Write-Host "`nPSObject is a special case in which PSObject will accept any object" } Write-Host Write-Host -ForegroundColor Blue "FOR EXAMPLE" Write-Host -ForegroundColor Blue "-----------" Write-Host "$FirstCmd --> (Object)[$FirstCmdType] " -NoNewline Write-Host -ForegroundColor Green " =====>> | =====>> " -NoNewline Write-Host "-$($SecondCmdParamByVal[0].Name)[$($SecondCmdParamByVal[0].Type)] --> $SecondCmd" Write-Host } function Show-ByPropertyName { Param ( $FirstCmd, $SecondCmd, $FirstCmdType, $FirstCmdProperties, $SecondCmdParamsByPN ) Clear-Host Write-Host -ForegroundColor Blue "ByPropertName Pipeline" Write-Host -ForegroundColor Blue "----------------------" Write-Host "The first command " -NoNewline Write-Host -ForegroundColor Red $FirstCmd -NoNewline Write-Host " produces an object of type " -NoNewline Write-Host -ForegroundColor Red $FirstCmdType Write-Host "However, the second command " -NoNewline Write-Host -ForegroundColor Red $SecondCmd -NoNewline Write-Host " does not allow " -NoNewline Write-Host -ForegroundColor Red $FirstCmdType -NoNewline Write-Host " objects " Write-Host "to be pipelined " -NoNewline Write-Host -ForegroundColor Blue "ByValue" -NoNewline Write-Host " via any of the parameters" Write-Host Write-Host "Therefore the pipeline now tries to pipe " -NoNewline Write-Host -ForegroundColor Blue "ByPropertyName" -NoNewline Write-Host " in which property values " Write-Host "from the first command are pipelined into the parameters of the second command" Write-Host "on the condition that they are both spelt the same and both have the same data types" Write-Host Write-Host -ForegroundColor Blue "FOR EXAMPLE" Write-Host -ForegroundColor Blue "-----------" '{0,-50}{1,13}{2,23}{3,-50}' -f "First Command","","","Second Command" '{0,-50}{1,13}{2,23}{3,-50}' -f "-------------","","","--------------" '{0,-50}{1,13}{2,23}{3,-50}' -f $FirstCmd,"","",$SecondCmd Write-Host '{0,-50}{1,13}{2,23}{3,-50}' -f "Properties","","","Matching Parameters" '{0,-50}{1,13}{2,23}{3,-50}' -f "----------","","","-------------------" $MatchingPropsToParams = @() foreach ($FirstCmdProp in $FirstCmdProperties) { if ($FirstCmdProp.Name -in $SecondCmdParamByPN.Name) { $MatchingParam = $SecondCmdParamsByPN | Where-Object {$_.Name -eq $FirstCmdProp.Name} if ($FirstCmdProp.Type -eq $MatchingParam.Type) { $MatchingPropsToParams += "$($FirstCmdProp.Name)[$($FirstCmdProp.Type)],=====>|=====>, ,$($MatchingParam.Name)[$($MatchingParam.Type)]" } } } If ($MatchingPropsToParams.Count -gt 0) { foreach ($MatchingPropToParam in $MatchingPropsToParams) { '{0,-50}{1,13}{2,23}{3,-50}' -f $MatchingPropToParam.split(',') } } else { Write-Host "There are no matches, so that means that pipelining ByValue or ByPropertName is not possible" } } # MAIN CODE # First lets see if ByValue will work, if not, lets see of ByPropertyName will work $FirstCmdOutputType = Get-ObjectType -Command $FirstCommand $SecondCmdParamByVal = Get-ObjectMember -Command $SecondCommand -PipelineMethod ByValue if ($FirstCmdOutputType -in $SecondCmdParamByVal.Type) { $ByValMatchingParam = $SecondCmdParamByVal | Where-Object {$_.Type -eq $FirstCmdOutputType} Show-ByVal -FirstCmd $FirstCommand -SecondCmd $SecondCommand -FirstCmdType $FirstCmdOutputType -SecondCmdParam $ByValMatchingParam } elseif ($SecondCmdParamByVal.Type -eq 'PSObject') { $ByValMatchingParam = $SecondCmdParamByVal | Where-Object {$_.Type -eq 'PSObject'} Show-ByVal -FirstCmd $FirstCommand -SecondCmd $SecondCommand -FirstCmdType $FirstCmdOutputType -SecondCmdParam $ByValMatchingParam } else { # If not, lets see of ByPropertyName will work $FirstCmdOutputProperties = Get-ObjectProperty -Command $FirstCommand $SecondCmdParamByPN = Get-ObjectMember -Command $SecondCommand -PipelineMethod ByPropertyName $ShowPNSplat = @{ FirstCmd = $FirstCommand SecondCmd = $SecondCommand FirstCmdType = $FirstCmdOutputType FirstCmdProperties = $FirstCmdOutputProperties SecondCmdParamsByPN = $SecondCmdParamByPN } Show-ByPropertyName @ShowPNSplat } } |