开发者

powershell indentation

I'm writing a large script that deploys an application. This script is based on several nested function calls.

Is there any way to "ident" the output based on the depth?

For example, I have:

function myFn()
{
    Write-Host "Start of myfn"
    myFnNested()
    Write-Host "End of myfn"
}
function myFnNested()
{
    Write-Host "Start of myFnNested"
    Write-Host "End of myFnNested"
}

Write-Host "Start of myscript"
Write-Host "End of myscript"

The output of the script will be :

Start of myscript
Start of myfn
Start of myfnNested
End of myFnNested
End of myFn
End of myscript

What I want to achieve is this output :

Start of mysc开发者_开发知识库ript
  Start of myfn
    Start of myfnNested
    End of myFnNested
  End of myFn
End of myscript

As I don't want to hardly code the number of spaces (since I does not know the depth level in complex script). How can I simply reach my goal ?

Maybe something like this?

function myFn()
{
    Indent()
    Write-Host "Start of myfn"
    myFnNested()
    Write-Host "End of myfn"
    UnIndent()
}
function myFnNested()
{
    Indent()
    Write-Host "Start of myFnNested"
    Write-Host "End of myFnNested"
    UnIndent()
}

Write-Host "Start of myscript"
Write-Host "End of myscript"


You could use a wrapper function around write-host which used $MyInvocation to determine the stack depth to create a number of spaces to prefix the message.

Combine this with the -scope ‹n› parameter of Get-Variable to pull out each calling level… something like the showstack function adapted from Windows PowerShell In Action (Payette, 1st Ed):

function ShowStack {
  trap { continue; }
  0..100 | Foreach-Object {
    (Get-Variable -scope $_ 'MyInvocation').Value.PositionMessage -replace "`n"
  }
}

You'll need the maximum value of $_ in the pipeline before Get-Variable fails for scope count being too high.


Check out this script http://poshcode.org/scripts/3386.html

If you load up that Write-Verbose wrapper, you can set $WriteHostAutoIndent = $true and then just call Write-Host and it will be indented based on stack depth. So given these functions as you defined them originally:

function myFn()
{
   Write-Host "Start of myfn"
   myFnNested
   Write-Host "End of myfn"
}
function myFnNested()
{
   Write-Host "Start of myFnNested"
   Write-Host "End of myFnNested"
}

With no changes, you can just dot-source a script file with that Write-Host wrapper function in it:

C:\PS> . C:\Users\Jaykul\Documents\WindowsPowerShell\PoshCode\3386.ps1

And then merely set the preference variable before you call your function:

C:\PS> $WriteHostAutoIndent = $true
C:\PS> myFn
  Start of myfn
    Start of myFnNested
    End of myFnNested
  End of myfn

Beautiful indented output, like magic ;-)


You can use Console.CursorLeft to set its position. Be aware that write-output will reset any custom location, so you need to reset it after each output. Here is a sample:

    $i = 0
    function Indent() {
        [console]::CursorLeft += 2
        $i = [console]::CursorLeft
        $i
    }

    function UnIndent() {
        if($i -gt 0) { $i -= 2 }
        [console]::CursorLeft = $i
        $i
    }

    function WriteIndent([string]$s) {
        [console]::CursorLeft += $i
        write-host $s
        # Reset indent, as write-host will set cursor to indent 0
        [console]::CursorLeft += $i
    }

    function myFnNested() {     
      $i = Indent     
      WriteIndent "Start of myFnNested"     
      WriteIndent "End of myFnNested"     
      $i = UnIndent 
    }

    function myFn() {     
      $i = Indent   
      WriteIndent "Start of myfn"     
      myFnNested
      WriteIndent "End of myfn"     
      $i = UnIndent 
    } 

    WriteIndent "Start of myscript"
    myFn
    WriteIndent "End of myscript"

Output:

    PS C:\scripting> .\Indent-Output.ps1
    Start of myscript
        Start of myfn
            Start of myFnNested
            End of myFnNested
        End of myfn 
    End of myscript


Write a DEBUG function. Two arguments, one is a flag that takes Start, Stop, or Note; the other argument should be the debug text. (I'd use 1, -1, and 0 for the flag, then you can just add the flag to a Indent variable to set increment depth. Stupid but good enough for debugging.)


I used another line of thinking. I set a count variable that is incremented at the start of the function and then used the following code to pad the line I was writing:

write-host ($string).PadLeft( ($string).length + (2 * $count) )

This will indent two spaces for every recursion.


You can override Write-Host to write your indents before calling the original with the '&' operator and the namespace. You might be able to derive the amount of indentation from the current stack scope, but a global variable gives you more control.

$global:writeHostIndent

function Write-Host
{
    Microsoft.PowerShell.Utility\Write-Host (' ' * $global:writeHostIndent) -NoNewline
    & 'Microsoft.PowerShell.Utility\Write-Host' $args
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜