[H-GEN] Humbug Obfusticated Awk Code Contest?

In memory of Mighty s335810 at student.uq.edu.au
Sun Dec 14 11:00:24 EST 1997


On Sun, 14 Dec 1997, Anthony Towns wrote:

> Today's lesson is entitled ``Unreadable code is easy with Awk''

Hmm, a little formatting would be nice:

#!/bin/sh
last | 

  awk '
    # Look for lines with time ranges on them, and print the user name.
    # Also print out the last field, containing the time for each entry.
    /[012][0-9]:[0-5][0-9] - [012][0-9]:[0-5][0-9]/
          {print $1 " " $NF}
  ' | 

  # Change all the colons and plusses (in times) for spaces.
  # Remove all parentheses
  sed 's/[:+]/ /g;s/[()]//g' | 

  awk '
    {
     # Add up times into a number of minutes
     if ( NF == 4 ) {
       # If there are four fields then there is also a number of days entry.
       M = (1200*$2 + 60*$3 + $4)
      } else {
       M = (60*$2 + $3)
      }

     # Send this new processing onto the next script
     print $1 " " M
    }' |

  # Ensure all of the same logins are together
  sort | 

  awk '
    BEGIN {
      # this is implicit, but what the hey.
      X = ""
     }
    
     {

      if ( X == $1 ) {
        # If the login name is the same as the last, keep placing the numbers
        # on this same line.  This has the effect of compacting each of the
        # series of entries with the same login name into one entry.

        for ( i = 2; i <= NF; i++ )
          printf( " %s", $i )

       } else {
        # If the login name is not the same as the last one, then finish the
        # last line with a newline character, and print the contents of the
        # current line.

        if ( X != "" )
          print ""
        printf( "%s", $0 )
        X = $1

       }

     }' |

  awk '
   {
    # print out the login name
    printf( "%-8s\t", $1 )

    # Add all the second counts on the end of the line passed from the previous
    # script, into a total number of minutes.
    M=0
    for ( i = 2; i <= NF; i++ ) {
      M += $i
     }

    # Convert into Hours:Minutes:Seconds format.
    H = int(M / 60)
    D=int(H/24)
    H %= 24
    M %= 60

    # Print result
    printf( "%4d+%02d:%02d\n", D, H, M )
   }' | 

  # Print in order according to the amount of online time.
  sort +.10 -r


	It would be even easier to read with slighly nicer variable names.
This is not really a typical use of awk, and incurrs alot of overhead with
pipes etc.  I'd personally write it a little more like this if it were
something I do often:

#!/bin/sh

last |

awk '

 # time is a lenght of time in either of the following formats:
 # (hh:mm)
 # or
 # (dd+hh:mm)
 # Where dd is a number of days
 #       hh is a number of hours
 #       mm is a number of minutes.

 function time2num(time,                splittime, returnnum) {

   # Break up the time into an array
   split(time, splittime, "[()+:]")

   if (splittime[4] != "") {
     # The array is broken into five strings, hence is in
     # (dd+hh:mm) format.
     returnnum = splittime[2] * 24 * 60 \
                + splittime[3] * 60 \
                + splittime[4]

    } else {
     # The array is in (hh:mm) format.
     returnnum = splittime[2] * 60 \
                + splittime[3]
    }

   return returnnum

  }

  # (almost) reverse of num2time.
  # This outputs in the standard format
  # dddd+hh:mm
  # Its stolen straight from ajs version :)

 function num2time(num,         Days, Hours, Minutes) {

    # Convert into Hours:Minutes:Seconds format.
    Hours = int(num / 60)
    Days=int(Hours/24)
    Hours %= 24
    Minutes = num % 60

    return sprintf( "%4d+%02d:%02d\n", Days, Hours, Minutes )

  }

 {

  # Update the time for the current username, by simply
  # adding it the users entry in the array.
  timesofar[$1] += time2num($NF)

 }

END {

  # Print all the users
  for (user in timesofar) {
    printf( "%-8s\t%s", user, num2time(timesofar[user]) )
   }

 }

' |

 # Sort for user convinience.  aj's sort option didn't seem to do anyting
 # particularly product on the solars system I'm working on, so here's
 # a simple sort by username :)
 sort


Some statistics:
bash-2.01$ last | wc
    7957   79306  588573

bash-2.01$ time ./online 
real    0m20.520s
user    0m9.662s
sys     0m2.092s

Individual breakdowns:

last:
real       20.2
user        5.7
sys         1.2

gawk:
real       20.7
user        3.6
sys         0.3

sort:
real       20.4
user        9.5
sys         1.7

	All tests performed on the UQCSEE machine fife, at or around 1am
Monday Dec 15.

> What's it do?

> Tells you the cumulative amount of time each of the system's users has
> logged on for.

> How does it do it?
> Consider that your homework for the day.

	A bit easy don't you think?

	My answer to Byron?  Awk rocks.

	My answer to whoever's about to flame me for posting to general
instad of chat?  It relates pretty directly to my talk on Saturday, so
I've got the right.  Honest.  It's like the law or something.  You don't
get enough email anyway ;)

	I do apologise for the length though...

	In memory of Mighty <memory at techie.com>

            ///                 ///  ///   ///  ///   /// ///      // // //
           ///        ///      ///        ///        ///          // // //
                              ///        ///        /// ///      // // //
         ///      ///  ///   ///        ///            ///
        ///                 /////////  /////////  /// ///      // // //

        I will not play it.  This game of loneliness
        Web page at http://student.uq.edu.au/~s335810

----------------------- HUMBUG General List --------------------------------
echo "unsubscribe general" | mail majordomo at humbug.org.au # To Unsubscribe



More information about the General mailing list