Reputation: 4070
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
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
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
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