YorSubs
YorSubs

Reputation: 4070

PowerShell Object that is static

I can create a reference like $x = Get-Process then I can call that with $x. That's great and the processes are updated every time that I type $x, but for what I'm doing I don't want that. I want to grab a snapshot of the processes at a point in time and then be able to query all properties of that capture as it was at that point in time.

So, I'm looking for a way to declare $x in such a way that collects everything at invocation and then when I reference $x, it contains the historic values at that point in time without re-processing Get-Process. How would I do that?

Upvotes: 1

Views: 205

Answers (3)

Vish
Vish

Reputation: 466

Can you show an example for the variable being updated? In my case, I did the following:

$x = Get-Process

I opened up notepad after declaring this variable, and did the following:

$x | Where-Object { $_.Name -contains 'notepad' }

This variable did not contain any processes by the name of notepad. But typing the following command (with notepad still open):

( Get-Process ) | Where-Object { $_.Name -contains 'notepad' }

Gave the following output:

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
    224      12     2652      13088       0.17   5876   2 notepad

Upvotes: 1

Tomalak
Tomalak

Reputation: 338278

Get-Process returns an array of Process objects. The array length will not change, so if you start another process, it will not show up in your $x. But each of the Process objects will update itself live.

To get a snapshot, you can use Select-Object to single out a couple of properties. Select-Object will create PSCustomObject instances that contain copies of the live values and they will no longer update themselves.

$x = Get-Process | Select-Object Id, ProcessName, StartTime

Get-Process alone is very fast, because it does not actually look at the process properties. If you combine that with Select-Object, the call takes longer, because every selected property of every process must be retrieved and copied. Only select the properties you are interested in, otherwise the call will take a very long time.

Upvotes: 2

Theo
Theo

Reputation: 61148

Looks like what you want is to make a clone of an object. To do that, there is a simple way:
$y = $x | Select-Object *, but that will not always suffice because it will leave you with a shallow copy.

To be able to also make a deep copy, you may try this function:

function Clone-Object ([object]$obj, [switch]$DeepClone) {
    if ($DeepClone) {
        # create a deep-clone of an object
        # test if the object implements the IsSerializable Interface
        if ([bool]($obj.GetType().IsSerializable)) {      # or: if ([bool]($obj.GetType().Attributes -band 'Serializable')) {
            $ms = New-Object System.IO.MemoryStream
            $bf = New-Object System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
            $bf.Serialize($ms, $obj)
            $ms.Position = 0
            $clone = $bf.Deserialize($ms)
            $ms.Close()
        }
        else {
            # try PSSerializer that serializes to CliXml
            # source: https://stackoverflow.com/a/32854619/9898643
            try {
                $clixml = [System.Management.Automation.PSSerializer]::Serialize($obj, 100)
                $clone  = [System.Management.Automation.PSSerializer]::Deserialize($clixml)
            }
            catch {
                Write-Error "Could not Deep-Clone object of type $($obj.GetType().FullName)"
            }
        }
    }
    else {
        # create a shallow copy of the same type
        # if the object has a Clone() method
        if ($obj -is [System.ICloneable]) {
            $clone = $obj.Clone()
        }
        # test if the object implements the IEnumerable Interface
        elseif ($obj -is [System.Collections.IEnumerable]) {
            try {
                # this only works for objects that have a parameter-less constructor
                $clone = New-Object -TypeName $($obj.GetType().FullName) -ErrorAction Stop
                foreach ($pair in $obj.GetEnumerator()) { $clone[$pair.Key] = $pair.Value }
            }
            catch {
                Write-Error "Could not Clone object of type $($obj.GetType().FullName)"
            }        
        }
        # not Cloneable, not Enumerable..
        else {
            # this returns an object with the properties copied, 
            # but it is NOT OF THE SAME TYPE as the source object
            $clone = $obj | Select-Object *
        }
    }

    return $clone
}

Use it like this:

$y = Clone-Object $x -DeepClone

Hope that helps

Upvotes: 4

Related Questions