Searching the output of last

Larry Wall lwall at jpl-devvax.jpl.nasa.gov
Mon Apr 29 17:22:06 AEST 1991


In article <1991Apr29.053721.8733 at agate.berkeley.edu> raymond at math.berkeley.edu (Raymond Chen) writes:
: In article <1991Apr29.043041.20454 at casbah.acns.nwu.edu>, navarra at casbah (John 'tms' Navarra) writes: 
: 
: >how bout setting up a program that... looks thru utmp by doing a last
: >| grep time (might be slow) ... then increments it by one for interval loops.
:               ^^^^^^^^^^^^^
:               my vote for understatement of the day.  It is not unheard
: of for people to be logged in for days at a time.  Incrementing by one means
: you have to perform thousands of greps.
: 
: [For comp.lang.perlers, the problem is to write a progral called `whenwho'
: which prints out everybody who was logged on at the time indicated on the
: command line.]
: 
: Try this.  Hacked up in 15 minutes, minimally tested.  Part of the
: problem is that you never know if you've searched backwards far
: enough, because there might be someone who has been logged on
: continuously for the past five months.  In the original problem,
: however, we are told that the wtmp goes back only as far as around 5am
: the day of the run, so this isn't an issue.
: 
[Script that reads wtmp directly omitted.]

If we can assume nobody is logged in overnight (or that records aren't kept),
then I'd just process the output of "last", like this:

#!/usr/bin/perl

$when = sprintf("%02d:%02d", shift =~ /^(\d+):?(\d\d)$/);
$today = (localtime())[3];
open(LAST, "last |");
while (<LAST>) {
    next if length() < 60;
    ($date,$in,$out) = unpack(x44A2xA5x3A5,$_);
    last if $date != $today;
    print if $when ge $in && $when le $out;
}

Not quite a one-liner, but getting closer...  Hmm...  If we throw out the
claptrap to check for todayness, and force people to enter exactly \d\d:\d\d
for the time, we can say

$w=shift;@ARGV="last|";$w ge substr($_,47,5)&&$w lt substr($_,55)&&print while<>

Hmm...  No reason not to use a regular expression...

$w=shift;@ARGV="last|";/.{47}(.{5})...(.*)/&&$w ge$1&&$w lt$2&&print while<>

Hmm...  We can dump two of those spaces that separate alphanumeric tokens...

$$=shift;@ARGV="last|";/.{47}(.{5})...(.*)/&&$$ge$1&&$$lt$2&&print while<>

Hmm...  We can assume that the first field contains the first colon...

$$=shift;@ARGV="last|";/(..:..)...(.*)/&&$$ge$1&&$$lt$2&&print while<>

I don't see how to make that shorter, offhand.  At least not in Perl alone.
Combining with shell, we can say

last|perl -pe '$_ x=/(..:..)...(.*)/&&"'$1'"ge$1&&"'$1'"lt$2'

That's gonna be tough for Randal to beat...  :-)

Larry



More information about the Comp.unix.questions mailing list