[comp.lang.perl] Cute hack around tiny bug

pvo@sapphire.OCE.ORST.EDU (Paul O'Neill) (10/28/90)

I really like Tim Chambers' df massager and Larry's rewrite of it.
-------------
#!/usr/local/bin/perl

open (BDF_PIPE, "bdf @ARGV |");

print <<End;
Filesystem                     Free (Mb) %Used
==============================|=========|=====
End

while (<BDF_PIPE>) {
    ($fs, $kbytes, $used, $avail, $capacity, $dirname) = split;
    next unless $kbytes > 0;
    # next unless $fs;          # uncomment to delete nfs filesystems
    $line{$avail} = sprintf("%30.30s%10.1f%6s\n",
                        $dirname . '.' x 30,
                        $avail / 1000, $capacity);
}

sub revnum { $b <=> $a; }

foreach $key (sort revnum keys(%line)) {
    print $line{$key};
}

print "\n";
----------------------

There's a tiny bug in there, though.

What if 2 file systems have the same available space? (ie. zero :-)

The old $line{$avail} gets wiped by the new $line{$avail} and is not
reported.

It occured to me that a good way to handle this was to add a small number,
say 0.1, to $avail if $line{$avail} aready existed.  But what if there were
*three* (or more) file systems with the exact same available space?

Hmm... What's a neat way of adding 0.1 to $avail recursively???

Bingo!! Don't bother.  Just add rand() to $avail!

      # next unless $fs;          # uncomment to delete nfs filesystems
+     $line{$avail} && $avail += rand;
      $line{$avail} = sprintf("%30.30s%10.1f%6s\n",

Hope you can use this some day ....

ps--the commented nfs remover just removes nfs mounts with long path's.


Paul O'Neill                 pvo@oce.orst.edu		DoD 000006
Coastal Imaging Lab
OSU--Oceanography
Corvallis, OR  97331         503-737-3251

mdb@ESD.3Com.COM (Mark D. Baushke) (10/29/90)

On 28 Oct 90 04:36:52 GMT, pvo@sapphire.OCE.ORST.EDU (Paul O'Neill) said:

Paul> I really like Tim Chambers' df massager and Larry's rewrite of it.
Paul> -------------
Paul> #!/usr/local/bin/perl

Paul> open (BDF_PIPE, "bdf @ARGV |");

Paul> print <<End;
Paul> Filesystem                     Free (Mb) %Used
Paul> ==============================|=========|=====
Paul> End

Paul> while (<BDF_PIPE>) {
Paul>     ($fs, $kbytes, $used, $avail, $capacity, $dirname) = split;
Paul>     next unless $kbytes > 0;
Paul>     # next unless $fs;          # uncomment to delete nfs filesystems
Paul>     $line{$avail} = sprintf("%30.30s%10.1f%6s\n",
Paul>                         $dirname . '.' x 30,
Paul>                         $avail / 1000, $capacity);
Paul> }

Paul> sub revnum { $b <=> $a; }

Paul> foreach $key (sort revnum keys(%line)) {
Paul>     print $line{$key};
Paul> }

Paul> print "\n";
Paul> ----------------------

Paul> There's a tiny bug in there, though.

Paul> What if 2 file systems have the same available space? (ie. zero :-)

Paul> The old $line{$avail} gets wiped by the new $line{$avail} and is not
Paul> reported.

I sent Larry private e-mail about this and suggested a one character
change to the script change 

     $line{$avail} = sprintf("%30.30s%10.1f%6s\n", ...);
to
     $line{$avail} .= sprintf("%30.30s%10.1f%6s\n", ...);

This works fine if you don't care about the order of two entries with
the same amount of available space.

Larry agreed and then suggested that another way to get around it
would be to use

     $line{$avail.'.'.$seq++} = sprintf("%30.30s%10.1f%6s\n", ...);

Paul> It occured to me that a good way to handle this was to add a
Paul> small number, say 0.1, to $avail if $line{$avail} aready
Paul> existed.  But what if there were *three* (or more) file systems
Paul> with the exact same available space?

Using a linearly increasing sequence number is probably a better
solution in this case. Even using rand() there is no guarentee that
you will not arrive at duplicate keys.

Enjoy!
-- 
Mark D. Baushke
mdb@ESD.3Com.COM

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (10/30/90)

In article <MDB.90Oct28220540@kosciusko.ESD.3Com.COM> mdb@ESD.3Com.COM (Mark D. Baushke) writes:
: I sent Larry private e-mail about this and suggested a one character
: change to the script change 
: 
:      $line{$avail} = sprintf("%30.30s%10.1f%6s\n", ...);
: to
:      $line{$avail} .= sprintf("%30.30s%10.1f%6s\n", ...);
: 
: This works fine if you don't care about the order of two entries with
: the same amount of available space.
: 
: Larry agreed and then suggested that another way to get around it
: would be to use
: 
:      $line{$avail.'.'.$seq++} = sprintf("%30.30s%10.1f%6s\n", ...);
: 
: Paul> It occured to me that a good way to handle this was to add a
: Paul> small number, say 0.1, to $avail if $line{$avail} aready
: Paul> existed.  But what if there were *three* (or more) file systems
: Paul> with the exact same available space?
: 
: Using a linearly increasing sequence number is probably a better
: solution in this case. Even using rand() there is no guarentee that
: you will not arrive at duplicate keys.

Personally, I like the .= approach the best--it's exactly equivalent to
the sequence number approach, and looks clean.  (Almost too clean--it's
easy to overlook the dot.)  And is a trick I've used quite a bit in the
past, so you can bet you'll run into the situation again.  In fact, if
you want to give the problem a label, it's "inverting on a non-unique key".
(I told you we should put it in the Book, Randal!  We still can...)

The Monte Carlo approach is fun, though.  Given the nature of rand(), I
don't think you're doing to get duplicate keys until you get 2**n filesystems
identically empty filesystems, where n is typically something like 16, 31
or 32.  But it does generate longer keys than a sequence number.

Larry

pvo@sapphire.OCE.ORST.EDU (Paul O'Neill) (10/30/90)

In article <MDB.90Oct28220540@kosciusko.ESD.3Com.COM> mdb@ESD.3Com.COM (Mark D. Baushke) writes:
>
>I sent Larry private e-mail about this and suggested a one character
>change to the script change 
>
>     $line{$avail} = sprintf("%30.30s%10.1f%6s\n", ...);
>to
>     $line{$avail} .= sprintf("%30.30s%10.1f%6s\n", ...);
>

Yikes!!  First it was one-liners.  Now it's one-byters!

What's next?  One-bitters?


Paul O'Neill                 pvo@oce.orst.edu		DoD 000006
Coastal Imaging Lab
OSU--Oceanography
Corvallis, OR  97331         503-737-3251

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (10/30/90)

In article <21417@orstcs.CS.ORST.EDU> pvo@sapphire.OCE.ORST.EDU (Paul O'Neill) writes:
: In article <MDB.90Oct28220540@kosciusko.ESD.3Com.COM> mdb@ESD.3Com.COM (Mark D. Baushke) writes:
: >
: >I sent Larry private e-mail about this and suggested a one character
: >change to the script change 
: >
: >     $line{$avail} = sprintf("%30.30s%10.1f%6s\n", ...);
: >to
: >     $line{$avail} .= sprintf("%30.30s%10.1f%6s\n", ...);
: >
: 
: Yikes!!  First it was one-liners.  Now it's one-byters!
: 
: What's next?  One-bitters?

Well, actually, one bit patches aren't all that uncommon.  Typically they
involve changing something from 0 to 1 or 1 to 0, or (un)capitalizing
something.  Here are a couple of one-bitters I sent out myself, recently:

***************
*** 733,739 ****
  	A(0,0,0),	/* NEXT */
  	A(0,0,0),	/* REDO */
  	A(0,0,0),	/* GOTO */
! 	A(1,1,0),	/* INDEX */
  	A(0,0,0),	/* TIME */
  	A(0,0,0),	/* TIMES */
  	A(1,0,0),	/* LOCALTIME */
--- 785,791 ----
  	A(0,0,0),	/* NEXT */
  	A(0,0,0),	/* REDO */
  	A(0,0,0),	/* GOTO */
! 	A(1,1,1),	/* INDEX */
  	A(0,0,0),	/* TIME */
  	A(0,0,0),	/* TIMES */
  	A(1,0,0),	/* LOCALTIME */
***************
*** 843,849 ****
  	A(1,1,0),	/* LISTEN */
  	A(1,1,0),	/* ACCEPT */
  	A(1,1,3),	/* SEND */
! 	A(1,1,1),	/* RECV */
  	A(1,1,1),	/* SSELECT */
  	A(1,1,1),	/* SOCKPAIR */
  	A(0,3,0),	/* DBSUBR */
--- 895,901 ----
  	A(1,1,0),	/* LISTEN */
  	A(1,1,0),	/* ACCEPT */
  	A(1,1,3),	/* SEND */
! 	A(1,1,3),	/* RECV */
  	A(1,1,1),	/* SSELECT */
  	A(1,1,1),	/* SOCKPAIR */
  	A(0,3,0),	/* DBSUBR */

Larry