YorSubs
YorSubs

Reputation: 4080

PowerShell Formatting Cmdlet Output

When I run the following Get-Command Get-ChildItem -Syntax I get:

<... empty line here ...>
Get-ChildItem [[-Path] <string[]>] [[-Filter] <string>] [-Include <string[]>] [-Exclude <string[]>] [-Recurse] [-Depth <uint32>] [-Force] [-Name] [-UseTransaction] [-Attributes <FlagsExpression[FileAttributes]>] [-Directory] [-File] [-Hidden] [-ReadOnly] [-System] [<CommonParameters>]
<... empty line here ...>
Get-ChildItem [[-Filter] <string>] -LiteralPath <string[]> [-Include <string[]>] [-Exclude <string[]>] [-Recurse] [-Depth <uint32>] [-Force] [-Name] [-UseTransaction] [-Attributes <FlagsExpression[FileAttributes]>] [-Directory] [-File] [-Hidden] [-ReadOnly] [-System] [<CommonParameters>]
<... empty line here ...>

However, this is not how I would like things to format and would prefer to strip the empty lines, and to prefix each syntax line with a # (so that when the lines wrap, it's clear where each syntax definition starts). This is just an example, want to do similar with many Cmdlet outputs etc, to be able to manipulate the output as text with no empty lines above, below or in between, just the text, formatted and compact, so would really appreciate in general how to format in this way. i.e.

# Get-ChildItem [[-Path] <string[]>] [[-Filter] <string>] [-Include <string[]>] [-Exclude <string[]>] [-Recurse] [-Depth <uint32>] [-Force] [-Name] [-UseTransaction] [-Attributes <FlagsExpression[FileAttributes]>] [-Directory] [-File] [-Hidden] [-ReadOnly] [-System] [<CommonParameters>]
# Get-ChildItem [[-Filter] <string>] -LiteralPath <string[]> [-Include <string[]>] [-Exclude <string[]>] [-Recurse] [-Depth <uint32>] [-Force] [-Name] [-UseTransaction] [-Attributes <FlagsExpression[FileAttributes]>] [-Directory] [-File] [-Hidden] [-ReadOnly] [-System] [<CommonParameters>]

Can anyone advise on an easy way to achieve formatting output like that please?

Upvotes: 1

Views: 218

Answers (3)

YorSubs
YorSubs

Reputation: 4080

Here was my current use for this. I find navigating Modules to be awkward and wanted a better/faster way to get a compact overview. I keep these in my personal Custom-Tools.psm1 Module and it helps me to very quickly see what's on a system.

Type mods to see all installed Modules and where they are located.

Type mods <partial_name> to see all matches. e.g. mods mic or mods soft

Type mod <module-name> to see quick details on the contents of a given Module.

Type mod <module-name> -i | more to get all syntax details on the contents of that Module.

I find this to be a very quick and convenient way to interrogate Modules. Hopefully of use to some folks.

mods

function mods ($search) {
    ""
    ":: Complete `$env:PSModulePath string:`n"
    $env:PSModulePath
    $env:PSModulePath -Split ";" -replace "\\+$", "" | sort
    ""
    ""
    $PathArray = $env:PSModulePath -Split ";" -replace "\\+$", "" | sort
    ""
    foreach ($Path in $PathArray) {
        if ($Path -ne '') {
            $Path = $Path.TrimEnd('\')
            echo ":: Modules under '$Path':`n"
            # if (Test-Path $Path) {
            $temp = (dir "$Path\*$search*" -Directory -EA Silent | Select -ExpandProperty Name) -join ", "   # mods w*  => mods w** which resolves fine
            if ($temp -eq "") { "No matches for '$search' exist in this path" }
            # } else { "This path is in `$env:PSModulePath but the folder does not exist"}
            Write-Wrap $temp
            ""
        }
    }
    ""
}

mod

function mod ($Module, $def, [switch]$ShowModulesHere, [switch]$Info) {
    if ($null -eq $Module) { "You must specify a Module to examine. Run 'mods' to see available Modules." ; break }
    ""
    if ([bool](Get-Module $Module -ListAvailable) -eq $true) {
        if ([bool](Get-Module $Module) -eq $true) { ":: Module '$Module' is already imported, so all functions are available`n" }
        else { ":: Module '$Module' was not imported, running import now ..." ; Import-Module $Module }
    }
    else { "Could not find Module in available Module folders`n" ; break }
    
    $ModulePath = ((Get-Module $Module | select Path).Path).TrimEnd('\')
    $ModuleVer = (Get-Module $Module -ListAvailable | select Version).Version | sls "\d"
    ":: '$Module' is version $($ModuleVer)`n`n$ModulePath"
    
    $ModuleRoot = Split-Path ((Get-Module $Module | select Path).Path).TrimEnd("\")
    $ModulesHere = (dir $Path -Directory | Select -ExpandProperty Name) -join ", "
    
    if ($Info) {
        ""
        foreach ($i in (Get-Command -Module $Module).Name) { 
            $out = $i   # Parse the info string from after the "{" 
            $type = "" ; try { $type = ((gcm $i -EA silent).CommandType); } catch { $deferr = 1 }
            $out += "   # $type"
            $syntax = Get-Command $i -Syntax
            $definition = "" ; if ($type -eq "Alias") { $definition = (get-alias $i).Definition }
            $syntax = $syntax -replace $definition, ""
            if ($type -eq "Alias") { $out += " for '$definition'" }
            $out
            if ($type -eq "Function") { $syntax = $syntax -replace $i, "" }
            if ($type -eq "Cmdlet") { $syntax = $syntax -replace $i, "" }
            if (!([string]::IsNullOrWhiteSpace($syntax))) { 
                $syntax -split '\r\n' | where {$_} | foreach { "Syntax =>   $_" | Write-Wrap }
            }
            ""
        }
        ""
    }
    else {
        ""
        ":: Module functions:"
        $out = ""; foreach ($i in (Get-Command -Module $Module).Name) { $out += " $i," } ; "" ; Write-Wrap $out.TrimEnd(", ")
        ""
    }
    $ModPaths = $env:PSModulePath -Split ";" -replace "\\+$", "" | sort
    ":: Module Paths (`$env:PSModulePath):"
    foreach ($i in $ModPaths) { "   $i"}
    ""
    ":: '$Module' Path:`n`n   $ModulePath"
    ""
    foreach ($i in $ModPaths) {
        if (!([string]::IsNullOrWhiteSpace($i))) {
           if ($ModulePath | sls $i -SimpleMatch) { $ModRoot = $i ; ":: `$env:PSModulePath parent location is:`n`n   $i" }
        }
    }
    ""

    if ($def -ne $null) {
        ":: Press any key to open '$def' definition:"
        pause
        ""
        def $def
        ""
    }

    if ($ShowModulesHere -eq $true) {
        ":: This `$env:PSModulePath root also contains the following Modules:"
        ""
        (dir $ModRoot -Directory | Select -ExpandProperty Name) -join ", "
        ""
    }
}

Upvotes: 0

YorSubs
YorSubs

Reputation: 4080

That's fantastic, thanks Doug, really useful. I also use a small function that assists me in nicely formatting line-wraps, so I can then use your code to get around the max width:

(Get-Command Get-ChildItem -Syntax) -split '\r\n' | where {$_} | foreach {"# $_"} | Write-Wrap

Where the Write-Wrap function always formats cleanly to the width of the console (edited with mklment0's points below, and with the essential PROCESS block that he pointed out):

function Write-Wrap {
    [CmdletBinding()]Param( [parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [Object[]]$chunk )
    PROCESS {
        $Lines = @()
        foreach ($line in $chunk) {
            $str = ''; $counter = 0
            $line -split '\s+' | % {
                $counter += $_.Length + 1
                if ($counter -gt $Host.UI.RawUI.BufferSize.Width) {
                    $Lines += ,$str.trim()
                    $str = ''
                    $counter = $_.Length + 1
                }
                $str = "$str$_ "
            }
            $Lines += ,$str.trim()
        }
        $Lines
    }
}

Upvotes: 0

Doug Maurer
Doug Maurer

Reputation: 8868

Here you go!

(Get-Command Get-ChildItem -Syntax) -split '\r\n' |
    where {$_} | foreach {"# $_"}

# Get-ChildItem [[-Path] <string[]>] [[-Filter] <string>] [-Include <string[]>] [-Exclude <string[]>] [-Recurse] [-Depth <uint32>] [-Force] [-Name] [-UseTransaction] [-Attributes <FlagsExpression[FileAttributes]>] [-Directory] [-File] [-Hidden] [-ReadOnly] [-System] [<CommonParameters>]
# Get-ChildItem [[-Filter] <string>] -LiteralPath <string[]> [-Include <string[]>] [-Exclude <string[]>] [-Recurse] [-Depth <uint32>] [-Force] [-Name] [-UseTransaction] [-Attributes <FlagsExpression[FileAttributes]>] [-Directory] [-File] [-Hidden] [-ReadOnly] [-System] [<CommonParameters>]

You will be bound by the max width of the console so even if they are one line it will wrap. You could pipe into clip if you want to paste it into something else.

(Get-Command Get-ChildItem -Syntax) -split '\r\n' |
    where {$_} | foreach {"# $_"} | clip

Upvotes: 2

Related Questions