Dumper.psm1 - A Data Dumper for Powershell

Dumper.psm1 - A Data Dumper for Powershell

The genesis of this effort came about because I wrote a PowerCLI report to look at the NetworkAdapter types on a bunch of VMs in order to break it down by OSFullName. I didn't spend a lot of time on the output formatting and the script took a while to run. During my traversal of VMs, I used a Hash of Hashes (HofH) to store my results. When finished, I thought I would just dump it out on the screen and have a look at the distributions.

Here was the format of my top level report structure - a HofH:

$ReportHash = @{ $osfullname = @{ $networkadaptertype = $count; ... }; ... }

Due to my limited Powershell skills, I couldn't construct a Select statement that gave me sane output without a lot of work - work which I had neglected to roll in to my long running report. You see I had my $ReportHash still sitting there defined and full of data in a powershell window but was kind of stuck getting the data out. The closest native thing I found was to do something like this:

$ReportHash.GetEnumerator() `

| Select Name, @{N="V";E={ $_.Value.GetEnumerator() | Select @{N="T";E={$_.Name}}, @{N="C";E={$_.Value}} }} `

| Format-Table -Auto *

The reason for the very short "N=" values was to preserve screen real-estate. This method got me close but it was still truncating the expansion of my nested hashes:

Name                                                       V

----                                                       -

Microsoft Windows Server 2003, Standard Edition (64-bit)   {@{T=Vmxnet3; C=13}, @{T=e1000; C=22}}

Microsoft Windows Server 2003, Enterprise Edition (64-bit) {@{T=Vmxnet3; C=1}, @{T=EnhancedVmxnet; C=1}}

Red Hat Enterprise Linux 5 (64-bit)                        {@{T=Vmxnet3; C=733}, @{T=EnhancedVmxnet; C=2}, @{T=Vmxnet; C=1}, @{T=Flexible; C=1}...}

...

I have since realized that  ConvertTo-XML would have been useful in this case but I didn't know that at the time. So then I started thinking about a way to solve the general case of printing out nested powershell data structures in a sane way without too much effort. Something I've grown accustomed to doing for a long time with another language.

Being a refugee from Perl, there are at least two things that I am used to:

  1. Since Perl has very generous anonymous reference and autovivification support, you find yourself cramming things in to nested data structures on the fly. Things like Hash of Arrays (HoA), Hash of Hashes (HofH), Array of Hashes (AofH), HofHofA, and any combination with surprising depth - very handy idiom. Powershell doesn't have autovivification AFAICT, but you can implement it yourself with a little bit of work.
  2. Most perl hackers I know don't use a debugger. A few print statements and the Data::Dumper module will normally suffice.

Since I am learning Powershell via the greatness of PowerCLI, it wasn't long before I was falling back in to old habits of nested data structures for generating reports and such as the above example attests. And I needed and went looking for something like a Data::Dumper for Powershell but couldn't find one. There is the ConvertTo-XML Cmdlet but I found it to be too over-bearing. The Dumper function described below was written to emulate some of what Perl's Data::Dumper does but for Powershell. It's not meant to be a port of Data::Dumper and is certainly not a production worthy cmdlet but I though it might be useful for others. I import it as a module and keep it hanging around in case I need it for debugging. Posting it here even though it's not specifically PowerCLI related. But without PowerCLI it wouldn't exist and it's the only thing I've used it with so here it is.

First the documentation from the Dumper function itself:

function Dumper {
<#
.SYNOPSIS  A nested hashtable/array Data Dumper for Powershell.
.DESCRIPTION A Data Dumper for Powershell nested hashtable/array structures
similar to what Perl's Data::Dumper does. Traverses an arbitrarily deep,
nested [hashtable] and/or [array] tree. Output is a [string]ified
representation of the tree in native powershell format or optionally, in
perl's dumper format. Converts anything it finds ToString()s or
GetType().ToString() so it is not a full featured Freeze/Thaw type utility.
It does not handle circular references and you will end up stuck in an infinite
loop so keep this in mind. This has only been tested to work with PS v2.0
but probably works with earlier versions as well.
.NOTES  Author:  Lance Braswell (http://www.linkedin.com/in/lancebraswell)
Twitter: @lance_braswell
.PARAMETER AsPerl
When set, the output format is suitable for a perl eval "$string" operation.
.PARAMETER ShowType
When set, the $Object.GetType().ToString() value of embedded objects is used
to represent them instead of the $Object.ToString() value.
.EXAMPLE
PS> Dumper @(1,2,3,4)
.EXAMPLE
PS> $array = @(1,2,3,4)
PS> Dumper $array
.EXAMPLE
PS> $hash = @{ "a" = "1"; "arr1" = @{ "aye" = "ayeval"; "bee" = "beeval"; `
                "cee" = "ceeval" }; "c "= "3"; "arr2" = $array }
PS> Dumper $hash
.EXAMPLE
PS> Connect-VIServer "myvc"
PS> Dumper @{ "vms" = @( Get-VM ); "datastores" = @( Get-Datastore ) }
.EXAMPLE
Go from PS to PS:
PS> $string = Dumper @("zero","one","two",3,4)
PS> Invoke-Expression $string
PS> $VAR1[2]
two
.EXAMPLE
Go from PS to perl:
PS> Dumper -AsPerl @("zero","one","two",3,4) | `
      perl.exe -e '$/=undef;$s=<>;$v=eval"$s";print $v->[2];'
two
PS> Dumper -AsPerl @("zero","one","two",3,4) | `
      perl.exe -MData::Dumper -e '$/=undef;$s=<>;$v=eval"$s";print Dumper($v);'
#>

A note about the -AsPerl switch to Dumper which turns a Powershell nested [array]/[hashtable] data structure in to output suitable for use with perl's eval "$string" behavior. Mainly this was done as an exercise in abstraction with output formatting and for the heck of it. I then used it to check my results to show that it was consistent behavior going from Powershell to Perl. But it's not something that I would recommend using to transfer data between the two though for purely text based leaf nodes in your Powershell data structure, it should work. It would also need some more work if embedded text has newlines or other formatting characters.


Here is some more extensive usage that is also included in the comments for the module.

# Some "unit testing". Take the stuff below and put it in a separate # script or paste it in to a window with Dumper.psm1 loaded. The names # below are made up. You will want to use your own VC for Connect-VIServer # of course.

Connect-VIServer -Server sjc-vc1 | Out-Null

"Creating an `$array with embedded newlines" | Out-Host

$array = 1,2,"A string with`nsome embedded newlines`n",4,5

"Dumper it as PS:" | Out-Host

Dumper $array

"Taking same `$array, Dumper it as PS, then use Invoke-Expression to go from a `$string back to PS" | Out-Host

$string = Dumper $array

"`$string is of Type:" | Out-Host

$string.GetType()

Invoke-Expression $string

"IEX'd `$string"

"`$VAR1 is of Type:" | Out-Host

$VAR1.GetType()

"`$VAR1 looks like:" | Out-Host

$VAR1

"Creating a `$hash with a nested hash and a nested `$array from above" | Out-Host

$hash = @{ "a" = "1"; `

           "baby" = @{ aye = "ayeval";

                         beeeeeeee = "beeval"; `

                       "cee" = "ceeval" }; `

           c = "3"; d = $array }

"Dumper it as PS:" | Out-Host

Dumper $hash

"Creating an AofHofA and AofA" | Out-Host

$array2 = @( $hash, 2, 3, 4, $array )

"Dumper it as PS:" | Out-Host

Dumper $array2

"Dumper it as Perl:" | Out-Host

Dumper -AsPerl $array2

"Use PowerCLI to build a tree of VMs and Datastores" | Out-Host

"Dumper it as PS:" | Out-Host

Dumper @{ "vms" = @( Get-VM ); "datastores" = @( Get-Datastore ) }

"Dumper it as Perl:" | Out-Host

Dumper -AsPerl @{ "vms" = @( Get-VM ); "datastores" = @( Get-Datastore ) }

"Dumper it as Perl, eval `"`$string`" it with perl, use perl's Data::Dumper to examine it. Compare with the above:" | Out-Host

Dumper -AsPerl @{ "vms" = @( Get-VM ); "datastores" = @( Get-Datastore ) } `

     | perl.exe -MData::Dumper -e '$/=undef;$s=<>;$v=eval"$s";print Dumper($v);'

# Unit testing output from the above:

@"

Creating an $array with embedded newlines

Dumper it as PS:

$VAR1 =

@(

  '1',

  '2',

  'A string with``nsome embedded newlines``n',

  '4',

  '5'

)

Taking same $array, Dumper it as PS, then use Invoke-Expression to go from a $string back to PS

$string is of Type:

IsPublic IsSerial Name                                     BaseType

-------- -------- ----                                     --------

True     True     String                                   System.Object

IEX'd $string

$VAR1 is of Type:

True     True     Object[]                                 System.Array

$VAR1 looks like:

1

2

A string with``nsome embedded newlines``n

4

5

Creating a $hash with a nested hash and a nested $array from above

Dumper it as PS:

$VAR1 =

@{

  'a' = '1';

  'c' = '3';

  'd' = @(

          '1',

          '2',

          'A string with``nsome embedded newlines``n',

          '4',

          '5'

         );

  'baby' = @{

             'aye' = 'ayeval';

             'cee' = 'ceeval';

             'beeeeeeee' = 'beeval'

            }

}

Creating an AofHofA and AofA

Dumper it as PS:

$VAR1 =

@(

  @{

    'a' = '1';

    'c' = '3';

    'd' = @(

            '1',

            '2',

            'A string with``nsome embedded newlines``n',

            '4',

            '5'

           );

    'baby' = @{

               'aye' = 'ayeval';

               'cee' = 'ceeval';

               'beeeeeeee' = 'beeval'

              }

   },

  '2',

  '3',

  '4',

  @(

    '1',

    '2',

    'A string with``nsome embedded newlines``n',

    '4',

    '5'

   )

)

Dumper it as Perl:

$VAR1 =

[

{

  'a' => '1',

  'c' => '3',

  'd' => [

          '1',

          '2',

          'A string with\\nsome embedded newlines\\n',

          '4',

          '5'

         ],

  'baby' => {

             'aye' => 'ayeval',

             'cee' => 'ceeval',

             'beeeeeeee' => 'beeval'

            }

},

'2',

'3',

'4',

[

  '1',

  '2',

  'A string with\\nsome embedded newlines\\n',

  '4',

  '5'

]

]

Use PowerCLI to build a tree of VMs and Datastores

Dumper it as PS:

$VAR1 =

@{

  'datastores' = @(

                   'sjc-1-vmhost-template',

                   'sjc-1-vmhost-nfs5',

                   'sjc-1-vmhost-nfs4',

                   'sjc-1-vmhost-nfs3',

                   'sjc-1-vmhost-nfs2',

                   'sjc-1-vmhost-nfs1',

                   'sjc-1-vmhost-8-local',

                   'sjc-1-vmhost-7-local',

                   'sjc-1-vmhost-6-local',

                   'sjc-1-vmhost-5-local',

                   'sjc-1-vmhost-4-local',

                   'sjc-1-vmhost-3-local',

                   'sjc-1-vmhost-1-local'

                  );

  'vms' = @(

            'sjc-vm-001',

            'sjc-vm-002',

            'sjc-vm-003',

            'sjc-vm-004',

            'sjc-vm-005',

            'sjc-vm-006',

            'sjc-vm-007'

           )

}

Dumper it as Perl:

$VAR1 =

{

'datastores' => [

                  'sjc-1-vmhost-template',

                  'sjc-1-vmhost-nfs5',

                  'sjc-1-vmhost-nfs4',

                  'sjc-1-vmhost-nfs3',

                  'sjc-1-vmhost-nfs2',

                  'sjc-1-vmhost-nfs1',

                  'sjc-1-vmhost-8-local',

                  'sjc-1-vmhost-7-local',

                  'sjc-1-vmhost-6-local',

                  'sjc-1-vmhost-5-local',

                  'sjc-1-vmhost-4-local',

                  'sjc-1-vmhost-3-local',

                  'sjc-1-vmhost-1-local'

                 ],

'vms' => [

           'sjc-vm-001',

           'sjc-vm-002',

           'sjc-vm-003',

           'sjc-vm-004',

           'sjc-vm-005',

           'sjc-vm-006',

           'sjc-vm-007'

          ]

}

Dumper it as Perl, eval "$string" it with perl, use perl's Data::Dumper to examine it. Compare with the above:

$VAR1 = {

          'datastores' => [

                            'sjc-1-vmhost-template',

                            'sjc-1-vmhost-nfs5',

                            'sjc-1-vmhost-nfs4',

                            'sjc-1-vmhost-nfs3',

                            'sjc-1-vmhost-nfs2',

                            'sjc-1-vmhost-nfs1',

                            'sjc-1-vmhost-8-local',

                            'sjc-1-vmhost-7-local',

                            'sjc-1-vmhost-6-local',

                            'sjc-1-vmhost-5-local',

                            'sjc-1-vmhost-4-local',

                            'sjc-1-vmhost-3-local',

                            'sjc-1-vmhost-1-local'

                          ],

          'vms' => [

                     'sjc-vm-001',

                     'sjc-vm-002',

                     'sjc-vm-003',

                     'sjc-vm-004',

                     'sjc-vm-005',

                     'sjc-vm-006',

                     'sjc-vm-007'

                   ]

        };

PS E:\lbraswel>

"@ > Out-Null

I believe the correct usage of this module would be as follows:

  1. Save the Dumper.psm1 attachment off to a suitable location.
  2. You may need to navigate to the saved file, right-click properties of the file, then choose UnBlock before you can import it.
  3. Import-Module the file like so:

PS> Import-Module C:\Path\To\Dumper.psm1

PS> Dumper @(1,2,3,4)

$VAR1 =

@(

  '1',

  '2',

  '3',

  '4'

)


I would certainly be grateful for any feedback.

Lance Braswell

@LanceBraswell

lance.braswell@gmail.com

Attachments
Comments

A useful script, thanks for sharing. I love the AsPerl switch.

You might be interested in NiTRo's solution, see his [PSH] listing récursif des propriétés d’un objet post. It's in French but I suppose Google Translate can help.

Version history
Revision #:
1 of 1
Last update:
‎01-02-2012 11:09 AM
Updated by: