4

I found that I do .GetType() and | Get-Member a lot, and sometimes I forget that it's .GetType and I try .Get-Type duh! error!), so I've been writing a function to try and gather that info quickly. This has proven to be quite useful when working on the console (I make sure to put the core command before each output so that I never forget the connection to real commands, so more of a techy summary to keep me connected to the language).

I'm curious if there are additional compound commands to extract useful generic information that we could use to report on the structure of a given object (things that we can quickly obtain in a nicely compact summary format even though more complex commands are required for some useful insight about given objects)?

$a = @(1,2,"x") ; obj $a. This returns 71 Methods (System.String and System.Int32) Types, so I've removed duplicates down to 50 (good to quickly see what is usable but maybe good to somehow also mention the different Types that are contained in that array?).

• Some input will break the function of course, but even a ScriptBlock example like this also works fine obj {$a; $x}. You can even do things like obj "".GetType() to see the Methods and properties in there.

• The use of .Module in the GetType() might be redundant as usually outputs CommonLanguageRuntimeLibrary, but maybe other useful information from these members (everything is useful at different times of course, but I'm curious for generic summary output)?

• In general any improvements or other compound commands that you use or could be useful to crack open object information in a quick summary view would be great to know? :-)

Updated with -Force that @Clint suggested:

function obj ($cmd) {
    if ($cmd -eq $null) { Write-Host "Object is `$null" ; return } 
    Write-Host "Contents:" -F Cyan
    $cmd
    ""
    Write-Host "(`$object).GetType()" -F Cyan -NoNewline ; Write-Host " :: [BaseType|Name|IsPublic|IsSerial|Module]"
    ($cmd).GetType() | % { "$($_.BaseType), $($_.Name), $($_.IsPublic), $($_.IsSerializable), $($_.Module)" }
    ""
    Write-Host "`$object | Get-Member -Force" -F Cyan
    $m = "" ; $p = "" ; $pp = "" ; $np = "" ; $sp = ""
    $msum = 0 ; $psum = 0 ; $ppsum = 0 ; $npsum = 0 ; $spsum = 0
    $($cmd | Get-Member -Force) | % {
        if ($_.MemberType -eq "Method") { if(!($m -like "*$($_.Name),*")) { $m += "$($_.Name), " ; $msum++ } }
        if ($_.MemberType -eq "Property") { if(!($p -like "*$($_.Name),*")) { $p += "$($_.Name), " ; $psum++ } }
        if ($_.MemberType -eq "ParameterizedProperty") { if(!($pp -like "*$($_.Name),*")) { $pp += "$($_.Name), " ; $ppsum++} }
        if ($_.MemberType -eq "NoteProperty") { if(!($np -like "*$($_.Name),*")) { $np += "$($_.Name), " ; $npsum++ } }
        if ($_.MemberType -eq "ScriptProperty") { if(!($sp -like "*$($_.Name),*")) { $sp += "$($_.Name), " ; $npsum++ } }
    }
    if($msum -ne 0) { Write-Host ":: Method [$msum] => $($m.TrimEnd(", "))" }
    if($psum -ne 0) { Write-Host ":: Property [$psum] => $($p.TrimEnd(", "))" }
    if($ppsum -ne 0) { Write-Host ":: ParameterizedProperty [$ppsum] => $($pp.TrimEnd(", "))" }
    if($npsum -ne 0) { Write-Host ":: NoteProperty [$npsum] => $($np.TrimEnd(", "))" }
    if($spsum -ne 0) { Write-Host ":: ScriptProperty [$spsum] => $($sp.TrimEnd(", "))" }
    ""
}

An example of output:

C:\> $a = @(123,"x")
C:\> def $a
Contents:
123
x

($object).GetType() :: [BaseType|Name|IsPublic|IsSerial|Module]
array, Object[], True, True, CommonLanguageRuntimeLibrary

$object | Get-Member -Force
:: Method [50] => CompareTo, Equals, GetHashCode, GetType, GetTypeCode, ToBoolean, ToByte, ToChar, ToDateTime, ToDecimal, ToDouble, ToInt16,
ToInt32, ToInt64, ToSByte, ToSingle, ToString, ToType, ToUInt16, ToUInt32, ToUInt64, Clone, Contains, CopyTo, EndsWith, GetEnumerator,
get_Chars, get_Length, IndexOf, IndexOfAny, Insert, IsNormalized, LastIndexOf, LastIndexOfAny, Normalize, PadLeft, PadRight, Remove, Replace,
Split, StartsWith, Substring, ToCharArray, ToLower, ToLowerInvariant, ToUpper, ToUpperInvariant, Trim, TrimEnd, TrimStart
:: Property [1] => Length
:: ParameterizedProperty [1] => Chars
YorSubs
  • 3,194
  • 7
  • 37
  • 60

2 Answers2

3

You have summarized it quite well as it is, you might as well add

  • object | gm -Force # To add members that are usually hidden by default
    

    The Get-Member command uses the Force parameter to add the intrinsic members and compiler-generated members of the objects to the display. Get-Member gets these members, but it hides them by default.

    Intrinsic members (PSBase, PSAdapted, PSObject, PSTypeNames)

    Compiler-generated get_ and set_ methods

    MSDN Doc

  • object.PSObject #Wraps the information pertaining to different members
    object.PSObject | Select-Object -ExpandProperty Properties #You can do the same for other members 
    
  • $object |gm -force -static #even with force the static members are not listed by default, so we need to explicitly mention static 
    
  • maybe good to somehow also mention the different Types that are contained in that array?).

    $object | ForEach-Object {$_.GetType().FullName}
    
  • OffTopic, additionally, if you want to capture the time taken for the function to execute

    $sw = [Diagnostics.Stopwatch]::StartNew()    
    $sw.Stop()
    Write-Host "Execution time" $sw.Elapsed.Milliseconds "ms"
    
Clint
  • 6,011
  • 1
  • 21
  • 28
  • 1
    Interesting, I didn't know that, that's one useful thing already then! Hopefully more tricks and completely different commands (or ways to optimise) to explore the objects are out there that folks can show me. I see that for an array, `-Force` exposes two extra Methods: `get_Chars, get_Length`, which are partly redundant due to `Chars` (ParameterizedProperty) and `Length` (Property), but good to know for sure. – YorSubs Feb 15 '20 at 10:06
  • @YorSubs hello just checking in to know if this answered your query or is there anything else you are looking to improve this, otherwise it would be great if you can mark this as resolved :) – Clint Mar 07 '20 at 17:15
  • It partly did for sure Clint. I was just wondering about the other big answer. I'm curious on leveraging PowerShell in ways like this and was considering doing a bounty on this question also. :) – YorSubs Mar 07 '20 at 17:36
  • @YorSubs, ahh alright, a reasonable bounty would be a wise move to attract attention :D – Clint Mar 08 '20 at 02:40
  • @YorSubs, Ill try to dig in some more on this subject and see what I can find ;) – Clint Mar 08 '20 at 03:24
  • @YorSubs, any feedback on the updated answer, hope it helps – Clint Mar 21 '20 at 07:31
3

Knowing that PowerShell isn't very clear in displaying an object (as e.g.: '', $Null, @() and @{$Null) all result in an empty line, or nothing at all), I think you need start with the first paragraph of you question and visualize the types and the structure of your objects in question and later concern with the methods which might differ on every level in the object structure.

*note: you might also reveal the types by using .PSTypeNames, which doesn't error on a $Null (presuming that strict mode isn't switch on).

Taken a little bit more advanced example: $a = @($Null, 1, '2', @(3))

Write-Host

The Write-Host cmdlet (used by your function) doesn't reveal much where is concerns types and the structure of the object: (Besides you should try to avoid Write-Host as it is Host specific)

PS C:\> $a
1
2
3

PS C:\> Write-Host $a
 1 2 3

To better understand such an object, you might want to serializes it. Quote from WikiPedia:

In computer science, in the context of data storage, serialization (or serialisation) is the process of translating data structures or object state into a format that can be stored (for example, in a file or memory buffer) or transmitted (for example, across a network connection link) and reconstructed later (possibly in a different computer environment)

This means that the string representation of the object basically contains all the information to rebuild (and to understand) the object (or at least to a certain level).

There are a few serializers that might come at hand here:

ConvertTo-Json

The ConvertTo-Json cmdlet converts any object to a string in JavaScript Object Notation (JSON) format a therefore might be handy to get an idea of the structure but not for the PowerShell types:

PS C:\> ConvertTo-Json $a
[
  null,
  1,
  "2",
  [
    3
  ]
]

ConvertTo-Xml

The ConvertTo-Xml cmdlet creates an XML-based representation of an object which is pretty verbose:

PS C:\> ConvertTo-Xml $a -as String
<?xml version="1.0" encoding="utf-8"?>
<Objects>
  <Object Type="System.Object[]">
    <Property />
    <Property Type="System.Int32">1</Property>
    <Property Type="System.String">2</Property>
    <Property Type="System.Object[]">
      <Property Type="System.Int32">3</Property>
    </Property>
  </Object>
</Objects>**strong text**

PSSerializer

This (.Net) class provides public functionality for serializing a PSObject and is internally used by PowerShell:

PS C:\> [System.Management.Automation.PSSerializer]::Serialize($a)
<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <Obj RefId="0">
    <TN RefId="0">
      <T>System.Object[]</T>
      <T>System.Array</T>
      <T>System.Object</T>
    </TN>
    <LST>
      <Nil />
      <Obj RefId="1">
        <I32>1</I32>
      </Obj>
      <S>2</S>
      <Obj RefId="2">
        <TNRef RefId="0" />
        <LST>
          <I32>3</I32>
        </LST>
      </Obj>
    </LST>
  </Obj>
</Objs>

ConvertTo-Expression

The ConvertTo-Expression cmdlet originated from the question: Save hash table in PowerShell object notation (PSON) and gives you a PowerShell representation of the object:

Implicit:

PS C:\> ConvertTo-Expression $a
$Null,
1,
'2',
(,3)

Explicit:

PS C:\> ConvertTo-Expression $a -Strong
[array](
        $Null,
        [int]1,
        [string]'2',
        [array][int]3
)

To explorer .Net classes that are not native to PowerShell:
*note that you might not reconstruct from -Explore expressions

PS C:\> Get-Service | Select -First 1 | ConvertTo-Expression -Strong -Explore
[System.ServiceProcess.ServiceController]@{
        'UserName' = [string]''
        'Description' = [string]'Runtime for activating conversational agent applications'
        'DelayedAutoStart' = [bool]$False
        'BinaryPathName' = [string]'C:\WINDOWS\system32\svchost.exe -k AarSvcGroup -p'
        'StartupType' = [Microsoft.PowerShell.Commands.ServiceStartupType]3
        'CanPauseAndContinue' = [bool]$False
        'CanShutdown' = [bool]$False
        'CanStop' = [bool]$False
        'DisplayName' = [string]'Agent Activation Runtime_131b90'
        'DependentServices' = [System.ServiceProcess.ServiceController[]]@()
        'MachineName' = [string]'.'
        'ServiceName' = [string]'AarSvc_131b90'
        'ServicesDependedOn' = [System.ServiceProcess.ServiceController[]]@()
        'StartType' = [System.ServiceProcess.ServiceStartMode]3
        'ServiceHandle' = $Null
        'Status' = [System.ServiceProcess.ServiceControllerStatus]1
        'ServiceType' = [System.ServiceProcess.ServiceType]224
        'Site' = $Null
        'Container' = $Null
}

To drill down in known (accelerated) PowerShell types:

PS C:\>Get-Date | Select-Object -Property * | ConvertTo-Expression
Date        : 1963-10-07 12:00:00 AM
DateTime    : Monday, October 7, 1963 10:47:00 PM
Day         : 7
DayOfWeek   : Monday
DayOfYear   : 280
DisplayHint : DateTime
Hour        : 22
Kind        : Local
Millisecond : 0
Minute      : 22
Month       : 1
Second      : 0
Ticks       : 619388596200000000
TimeOfDay   : 22:47:00
Year        : 1963
iRon
  • 20,463
  • 10
  • 53
  • 79
  • 1
    This is great @iRon. I see that my simplistic script completely breaks on a more complex object like `$a = @($Null, 1, '2', @(3))`. I'd probably never create such a complex object, but still, it would be nice to have a script that can deconstruct an object (at least as much as is reasonably feasible!). JSON is preferable to me for compactness, and this is much better than my "Contents" output so I'll definitely use `ConvertTo-Json` there and will use your other notes. Great things here thanks. – YorSubs Feb 16 '20 at 12:25