BlackForrest has asked for the wisdom of the Perl Monks concerning the following question:

Hi community,
i have of cause read some examples to do what i want to do, but nothing helps ...

I would output some Server details - in this case Memory and Process-PID/Name in a loop and then ring them into a table in a "sorted" way...
Let's start with the pattern matching:

if ($line=~ /(\d{1,3},\d{3,3},\d\d\d K)/){ # e.g. 3,373,560 K $processname=" $`"; # matches before string $memory = $& ; # $& matches the string

if i tell them to print i this loop everything is fine

print "$memory --- $processname <br>\n";

1788180 --- disp+work.exe 3380 Console 0
2787204 --- sqlservr.exe 1768 Console 0
1078120 --- jlaunch.exe 4608 0
etc..

then i push them in to Array @allprocesses

push(@allprocesses, $memory => " - $processname <br>",); $memory = ""; # empty variable $processname = ""; # empty variable print "<td width=\'400\' height=\'15\'>Unsorted Array: <br> @allproce +sses </td> \n" ;

print out this is also still o.k.

Unsorted Array
1830376 - sqlservr.exe 1812 0
488716 - disp+work.exe 4772 0
350060 - disp+work.exe 9712 0
396548 - disp+work.exe 9980 0
327372 - disp+work.exe 7892 0
397132 - disp+work.exe 2872 0
276124 - disp+work.exe 8948 0
159176 - disp+work.exe 7292 0
283332 - disp+work.exe 9620 0
199288 - disp+work.exe 7760 0
119540 - disp+work.exe 6376 0
433064 - disp+work.exe 8712 0
255120 - disp+work.exe 4548 0
507316 - disp+work.exe 7852 0
104272 - disp+work.exe 10208 0
119728 - SavService.exe 2336 0

Now i want to sort this array, or better run this "pairs" sorted to play around like ">500000 then make something..."

%memhash=@allprocesses; $anz=keys(%memhash); print "Pair of Hashes: $anz <br>\n"; foreach $key ( sort {$b <=> $a} values %memhash){ $help1 = " $keys$memhash{$keys}"; print "Memory: $key Name: $help1 <br> \n";

"sort" works, but where are my processname ($help1) gone?
Output:

Pair of Hashes: 16
Memory: 1830376 Name: 1830376
Memory: 507316 Name: 1830376
Memory: 488716 Name: 1830376
Memory: 433064 Name: 1830376
Memory: 397132 Name: 1830376
Memory: 396548 Name: 1830376
Memory: 350060 Name: 1830376
Memory: 327372 Name: 1830376
Memory: 283332 Name: 1830376
Memory: 276124 Name: 1830376
Memory: 255120 Name: 1830376
Memory: 199288 Name: 1830376
Memory: 159176 Name: 1830376
Memory: 119728 Name: 1830376
Memory: 119540 Name: 1830376
Memory: 104272 Name: 1830376

Also try to sort with array from the hash:

@sorted = sort({$b<=>$a}values(%memhash)); for ($i=0; $i<=$#sorted; $i++) { my $paar = $sorted[$i]; print "$paar = $memhash{$paar} <br>" ; } print "</td></tr>\n" ;

output:
sorted Array:
1830376 =
507316 =
488716 =
433064 =
397132 =
396548 =
350060 =
327372 =
283332 =
276124 =
255120 =
199288 =
159176 =
119728 =
119540 =
104272 =

Can someone find the bug?
Thanks to all,
Klaus

Replies are listed 'Best First'.
Re: transfer a array to a hash
by ww (Archbishop) on Jan 18, 2012 at 18:49 UTC

    Yes, by all means, let's take the regex first.
    Using the special vars you do,

    002: $processname=" $`";  # matches before string
    003: $memory = $&  ;     # $& matches the string

    may increase your cost-of-processing. See perlre at the "Warning" section of the "Capture Buffers" segment. As that doc now (as of 5.10 but perhaps earlier; certainly for 5.12) there's a bit of ambiguity about the cost of $` and $&:

    WARNING: Once Perl sees that you need one of $&, $`, or $' anywhere in the program, it has to provide them for every pattern match. This may substantially slow your program. Perl uses the same mechanism to produ +ce $1, $2, etc, so you also pay a price for each pattern that contains capturing parentheses. (To avoid this cost while retaining the groupin +g behaviour, use the extended regular expression "(?: ... )" instead.) B +ut if you never use $&, $` or $', then patterns *without* capturing parentheses will not be penalized. So avoid $&, $', and $` if you can, but if you can't (and some algorithms really appreciate them), once you've used them once, use them at will, because you've already paid t +he price. As of 5.005, $& is not so costly as the other two.

    Others, deeper into benchmarking, or perlguts, may be able to clarify, but based on the traditional warning against those special regex vars, you might chose ordinary captures:

    #!/usr/bin/perl use strict; use warnings; use 5.012; # 948541 my @arr = <DATA>; for $_(@arr) { if ( $_ =~ /([a-z 0-9\+\.]+?)(\d{0,3},{0,1}\d{0,3},{0,1}\d{1,3}) K +/i ) { #1 my $processname = $1; my $memory = $2; say "$memory --- $processname"; } } #1 NB this regex allows for entries as small as 1K -- a condition # of which you might want to be aware. =head output: 1,788,180 --- disp+work.exe 3380 Console 0 2,787,204 --- sqlservr.exe 1768 Console 0 1,078,120 --- jlaunch.exe 4608 0 1,830,376 --- sqlservr.exe 1812 0 488,716 --- disp+work.exe 4772 0 17 --- proc 9412 Console 0 =cut __DATA__ disp+work.exe 3380 Console 0 1,788,180 K sqlservr.exe 1768 Console 0 2,787,204 K jlaunch.exe 4608 0 1,078,120 K sqlservr.exe 1812 0 1,830,376 K disp+work.exe 4772 0 488,716 K small_proc 9412 Console 0 17 K

    Is your data sample correct, showing "Console" present in some entries and not in others?

      Correct! Using these special regex variables is not advised due to the performance decrease.

      When I wrote my code, I didn't look closely at the way that these lines were generated and how the @array was built.

      But to generate the @array, with this DATA, here is one way....

      #!/usr/bin/perl -w use strict; my @data; foreach my $line (<DATA>) { # anchor match to the end of string with $ # anchor match to the beginning of string with ^ # ignore the K and spaces at the end (includes \n) # get the numbers and commas right before the K # get this stuff in front ($before) # but ignore any leading spaces in the line # a "greedy" match will not be so "greedy" that it # won't allow the last part of the regex to match # so it is not necessary to constrain the .+ match here my ($before, $mem) = ($line =~ /^\s*(.+)\s+([\d,]+)\s*K\s*$/); $mem =~ tr/,//d; #delete commas in like 2,787,204 push @data, "$mem $before\n"; #adds the \n back in } print @data; =prints: 1788180 disp+work.exe 3380 Console 0 2787204 sqlservr.exe 1768 Console 0 1078120 jlaunch.exe 4608 0 1830376 sqlservr.exe 1812 0 488716 disp+work.exe 4772 0 17 small_proc 9412 Console 0 =cut __DATA__ disp+work.exe 3380 Console 0 1,788,180 K sqlservr.exe 1768 Console 0 2,787,204 K jlaunch.exe 4608 0 1,078,120 K sqlservr.exe 1812 0 1,830,376 K disp+work.exe 4772 0 488,716 K small_proc 9412 Console 0 17 K

      Hi, yes the "console " entry appears if process is running under Windows Console process

        Hey,
        thanks to all, finally i could fix it..
        i renamed all $variales and came around some unclear assignments

        %memhash=@allprocesses; $anz=keys(%memhash); print "Pair of Hashes: $anz <br>\n"; foreach $memkey ( sort{$b<=>$a}keys %memhash){ $name = "$values$memhash{$memkey}"; if ($memkey <500000) { + # value less then 500.000 byte print "$memkey -- Name: $name \n"; } else { + # values above 500.000 byte in Red +print "<h4>$memkey </h4>-- Name: $name \n"; }
Re: transfer a array to a hash
by Marshall (Canon) on Jan 18, 2012 at 14:48 UTC
    foreach $key ( sort {$b <=> $a} values %memhash){
    here $key is not the hash key, it is one of the values!

    foreach my $key sort{$memhash{$a} <=> $memhash{$b}}keys %memhash
    would sort keys according to the values of those keys

    At the root of your problem is the conversion from array to hash is flawed. However, I don't see why you need a hash. You can just sort your array.

    #!/usr/bin/perl -w use strict; my @array = <DATA>; @array = sort {my $mema = (split(' ',$a))[0]; my $memb = (split(' ',$b))[0]; $mema <=> $memb }@array; print @array; print "\n"; #to get >500000, use grep print grep{(split)[0] > 500000}@array; =prints 104272 - disp+work.exe 10208 0 119540 - disp+work.exe 6376 0 119728 - SavService.exe 2336 0 159176 - disp+work.exe 7292 0 199288 - disp+work.exe 7760 0 255120 - disp+work.exe 4548 0 276124 - disp+work.exe 8948 0 283332 - disp+work.exe 9620 0 327372 - disp+work.exe 7892 0 350060 - disp+work.exe 9712 0 396548 - disp+work.exe 9980 0 397132 - disp+work.exe 2872 0 433064 - disp+work.exe 8712 0 488716 - disp+work.exe 4772 0 507316 - disp+work.exe 7852 0 1830376 - sqlservr.exe 1812 0 507316 - disp+work.exe 7852 0 1830376 - sqlservr.exe 1812 0 =cut __DATA__ 1830376 - sqlservr.exe 1812 0 488716 - disp+work.exe 4772 0 350060 - disp+work.exe 9712 0 396548 - disp+work.exe 9980 0 327372 - disp+work.exe 7892 0 397132 - disp+work.exe 2872 0 276124 - disp+work.exe 8948 0 159176 - disp+work.exe 7292 0 283332 - disp+work.exe 9620 0 199288 - disp+work.exe 7760 0 119540 - disp+work.exe 6376 0 433064 - disp+work.exe 8712 0 255120 - disp+work.exe 4548 0 507316 - disp+work.exe 7852 0 104272 - disp+work.exe 10208 0 119728 - SavService.exe 2336 0
    Update: A clarification of making the memhash....
    There is kind of a duality between a hash and array. To make a hash keyed on the memory size, use split to feed pairs into %memhash.. However, keys to a hash have to be unique and I doubt that will be true in this case. I would leave this as an array as above.
    #%memhash=@allprocesses; #won't work my %memhash = map{chomp; (split(' ',$_,3))[0,2]}@array; #note: $array[1], the "-" is thrown away print Dumper \%memhash; $VAR1 = { '488716' => 'disp+work.exe 4772 0', '119728' => 'SavService.exe 2336 0', '283332' => 'disp+work.exe 9620 0', '199288' => 'disp+work.exe 7760 0', '276124' => 'disp+work.exe 8948 0', '255120' => 'disp+work.exe 4548 0', '1830376' => 'sqlservr.exe 1812 0', '396548' => 'disp+work.exe 9980 0', '159176' => 'disp+work.exe 7292 0', '507316' => 'disp+work.exe 7852 0', '119540' => 'disp+work.exe 6376 0', '397132' => 'disp+work.exe 2872 0', '350060' => 'disp+work.exe 9712 0', '327372' => 'disp+work.exe 7892 0', '104272' => 'disp+work.exe 10208 0', '433064' => 'disp+work.exe 8712 0' };
Re: transfer a array to a hash
by i5513 (Pilgrim) on Jan 18, 2012 at 14:37 UTC