3

I've been banging my head against the wall on this one.
I know that if I create an Array in Powershell, then copy the array, it'll copy it as a reference type not a value type.
So, the classic example is:

$c = (0,0,0)
$d = $c
$c[0] = 1
$d
1
0
0

The solution is to do $d = $c.clone() This isn't working though if the array itself is a collection of reference types. This is my problem. I'm trying to create an array to track CPU usage by creating an array of Processes, wait a while, then check the latest values and calculate the differences. However the Get-Process creates a reference array. So when I do the following:

$a = ps | sort -desc id | where-object {$_.CPU -gt 20} #Get current values
$b = $a.clone() #Create a copy of those values.
sleep 20 #Wait a few seconds for general CPU usage...

$a = ps | sort -desc id | where-object {$_.CPU -gt 20} #Get latest values.
$a[0]  
$b[0] #returns the same value as A.

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessNam
-------  ------    -----      ----- -----   ------     -- ----------
   3195      57    90336     100136   600    83.71   7244 OUTLOOK

$a and $b will always return the same value. If I try and do it one entry at a time using something like $b[0] = "$a[0].clone()" - PS complains that Clone can't be used in this case.

Any suggestions??

Also, just FYI, the second $a = PS |.... line isn't actually needed since $a is reference type to the PS list object, it actually gets updated and returns the most current values whenever $a is called. I included it to make it clearer what I'm trying to accomplish here.

jpaugh
  • 6,634
  • 4
  • 38
  • 90
EtanSivad
  • 487
  • 8
  • 16

2 Answers2

5

To copy an array, you can do the following:

$c = (0,0,0)
$d = $c | foreach { $_ }
$c[0] = 1
"c is [$c]"
"d is [$d]"

Result

c is [1 0 0]
d is [0 0 0]

For your particular issue (comparing CPU usage of processes), something more specific would probably be better, as Keith pointed out.

latkin
  • 16,402
  • 1
  • 47
  • 62
  • 2
    This doesn't work when the elements of the array are references to objects. `$d=$c|%{$_}` still copies references to the objects to $d, so when you change a property of the element in $c[0], you see the change in $d. – Cary Apr 18 '17 at 19:02
1

Technically $d = $c is not any sort of array copy (of reference or value). It's just stashing a reference to the array $c refers to, into $d. I think you only need to grab the array of processes once and then call the Refresh method. You'll have to check the Exited property first to make sure the associated process is still running. Of course, this won't help you if you're interested in any new process that start up. In that case, grab a snapshot of processes at different times, weed out all but the intersection of processes between the two arrays (by process Id) and then compute the differences in their property values - again based on process Id. Take make this easier, you might want to put each snapshot in a hashtable keyed off the process Id.

Keith Hill
  • 194,368
  • 42
  • 353
  • 369
  • Ahhh, of course. Good call. Really, all I need is the values of ID and CPUs. So I can dump these into a hash table with: foreach ($_ in $psCurrentList) { $psPreviousList.add($_.id,$_.cpu) } That works, thank you. – EtanSivad Sep 18 '12 at 18:53