Format-OneLine.psm1
function Format-OneLine { <# .SYNOPSIS Format the given object as a single-line string. .DESCRIPTION Format the given object as a single-line string. Will recurse into objects up to the given depth. Special exception for PSVariables, they will render even at depth 0, allowing. Only outputs on pipeline-end because it uses a pipeline-accumulator hack. This allows you to pipe multiple objects into Format-OneLine and have them all output as a single shared line. See example for the usefulness of these foibles WRT Get-Variable. .EXAMPLE ```powershell $foo = 'bar'; $baz = 'quux' Get-Variable foo, baz | Format-OneLine # returns "foo: bar; baz: quux" ``` #> [OutputType([string])] [CmdletBinding()] param ( [Parameter(ValueFromPipeline, Position = 0)] [object] $InputObject, # Depth to search to. Note that PSVariables are still iterated at depth 0, so that a pipeline of GetVariables [int] $Depth = 1, # Include the key/value pairs for null or otherwise empty objects. [switch] $ShowEmpty, # Wrap nested HashTables/PSObjects in `{}` [switch] $DoBraceNestedTables ) begin { $accumulator = [Collections.ArrayList]::new() } process { $accumulator.Add($InputObject) | Out-Null } end { $obj = if ($accumulator.Count -gt 1) { $accumulator } else { # unwrap single-element ArrayList. $accumulator | Select-Object -First 1 } Format-OneLineRecursive $obj $Depth $Depth -ShowEmpty:$ShowEmpty -DoBraceNestedTables:$DoBraceNestedTables } } Export-ModuleMember -Function * # private function below function Format-OneLineRecursive { <# .SYNOPSIS Recursive internal implementation of Format-OneLine with extra needed parameter(s). #> [OutputType([string])] [CmdletBinding()] param ( [Parameter(ValueFromPipeline, Position = 0)] [object] $InputObject, # Initial search depth. Used to compare against current depth for DoBraceNestedTables. [Parameter(Position = 1)] [int] $StartingDepth = 1, # Current depth. Note that PSVariables are still iterated at depth 0, so that a pipeline of GetVariables [Parameter(Position = 2)] [int] $CurrentDepth = $StartingDepth, # Include the key/value pairs for null or otherwise empty objects. [switch] $ShowEmpty, # Wrap nested HashTables/PSObjects in `{}` [switch] $DoBraceNestedTables ) begin { $paramTable = @{StartingDepth=$StartingDepth; ShowEmpty=$ShowEmpty; DoBraceNestedTables=$DoBraceNestedTables} } process { $resultList = [Collections.ArrayList]::new() foreach ($item in $InputObject) { Write-Verbose "Object '$item', CurrentDepth '$CurrentDepth'," # if the input object is a dictionary, convert it to a PSCustomObject so we can use its properties. # this is useful for converting HashTables to objects. if ($item -is [System.Collections.IDictionary]) { $item = [PSCustomObject]$item } $result = if ($null -eq $item) { $null } elseif ($item -is [Management.Automation.PSVariable] -and $CurrentDepth -ge 0) { "$($item.Name): $(Format-OneLineRecursive $item.Value -CurrentDepth($CurrentDepth - 1) @paramTable)" } elseif ($CurrentDepth -le 0) { # anything recursive above this line must have a depth-check to prevent infinite recursion. $item.ToString() } elseif ($item -is [Collections.IEnumerable]) { # if it's an IEnumerable, use its items ($item | ForEach-Object { if ($ShowEmpty -or $_) { Format-OneLineRecursive $_ -CurrentDepth ($CurrentDepth - 1) @paramTable } } ) -join '; ' } elseif ($item -is [Management.Automation.PSObject]) { # if it's a PSObject, use its properties $psObjectRendered = ($item.PSObject.Properties | ForEach-Object { if ($ShowEmpty -or $_.Value) { "$($_.Name): $(Format-OneLineRecursive $_.Value -CurrentDepth ($CurrentDepth - 1) @paramTable)" } } ) -join '; ' if ($DoBraceNestedTables -and $CurrentDepth -ne $StartingDepth) { $psObjectRendered = "{$psObjectRendered}" } # output $psObjectRendered } else { # if it's not a PSObject or IDictionary, just use ToString() $item.ToString() } # replace excess whitespace/newlines with a single space. $resultList.Add(($result -replace '\s+', ' ')) | Out-Null } # Then output. $resultList -join '; ' } } |