User Logged In The Most - Bash

Hi, first time poster, newbie to Bash. I'm looking to get the username of the user who's been logged into a computer the most / longest.

I am new to Bash but am familiar with other scripting languages, mainly PHP. So I have a general idea about how to go about the script logic, but don't know all the syntax. So far I can get the list of console users and their session times:

last | grep console | awk '{print $1 " " $9}'
user.name in
user.name (01:03)
user.name (2+00:05)
other.user (3+15:31)
user.name (08:09)
other.user (15:45)

Now I would basically need to:

  1. Loop through each session
    [list=1]
  2. Ignore any session time not formatted as (d+hh:mm), ex: current session
  3. Parse the session time and convert to minutes, ex: (2+03:12) = 3072
  4. Add users session time to their logged-in time (array of user => time)
    [/list]
  5. Sort array of users by their logged-in time
  6. Return the top/bottom username (depending on sort order)

I'm going to keep working at this but thought I'd post here in case anyone can help. Thanks for any help you can provide!

[edit] misunderstood.

We need an unprocessed sample of your last output.

Ok, I whipped up what a PHP script would look like. I know there are several ways to do this in PHP but this lists it step-by-step. Also, this would strictly be ran on Mac computers.

// See updated script below

I still need an unprocessed sample of your last output.

As in:

$ last
(it is a mystery)
$

obscure the usernames and Ip addresses and whatever but I need to see what it really looks like. Without that I can't whip up anything I know will work. I can't even tell why you're grepping for 'console' at this point (my 'last' output never contains it).

Sorry, didn't realize that's what you were asking for. Google also was confused when I searched for "bash last". :slight_smile: Here is what the output is on our Macs.

computer-name:~ panman82$ last
panman82  ttys000                   Thu Feb 17 11:00   still logged in
panman82  ttys000                   Thu Feb 17 08:25 - 09:31  (01:06)
panman82  ttys000                   Thu Feb 17 07:50 - 08:06  (00:16)
panman82  ttys000                   Thu Feb 17 07:44 - 07:47  (00:02)
panman82  ttys001                   Wed Feb 16 16:14 - 16:14  (00:00)
panman82  ttys000                   Wed Feb 16 11:27 - 16:26  (04:59)
panman82  ttys000                   Wed Feb 16 10:07 - 11:27  (01:19)
other.admin   ttys000  other.computer.com Tue Feb 15 13:11 - 13:12  (00:00)
panman82  ttys000                   Tue Feb 15 08:29 - 09:02  (00:32)
panman82  console                   Tue Feb 15 07:46   still logged in
reboot    ~                         Tue Feb 15 07:45 
shutdown  ~                         Fri Feb 11 16:15 
panman82  ttys000                   Wed Feb  2 09:03 - 13:39  (04:35)
panman82  ttys002                   Tue Feb  1 15:28 - 15:29  (00:01)
panman82  ttys001                   Tue Feb  1 10:31 - 16:02  (05:31)
panman82  ttys001                   Tue Feb  1 10:29 - 10:31  (00:01)
panman82  ttys001                   Tue Feb  1 10:28 - 10:29  (00:01)
panman82  ttys001                   Tue Feb  1 10:26 - 10:28  (00:01)
panman82  ttys001                   Tue Feb  1 10:25 - 10:26  (00:00)
panman82  ttys001                   Tue Feb  1 10:23 - 10:25  (00:02)
panman82  ttys001                   Tue Feb  1 10:22 - 10:22  (00:00)
panman82  ttys001                   Tue Feb  1 10:20 - 10:22  (00:01)
panman82  ttys001                   Tue Feb  1 10:00 - 10:20  (00:20)
panman82  ttys000                   Tue Feb  1 08:44 - 16:02  (07:18)
panman82  ttys000                   Mon Jan 31 15:40 - 16:04  (00:23)
panman82  ttys000                   Mon Jan 31 13:49 - 15:40  (01:50)
panman82  ttys000                   Mon Jan 31 08:33 - 10:11  (01:37)
panman82  console                   Mon Jan 31 08:31 - 16:15 (11+07:43)
reboot    ~                         Mon Jan 31 08:30 
shutdown  ~                         Fri Jan 28 16:04 
panman82  ttys000                   Mon Jan 24 11:16 - 11:16  (00:00)
panman82  console                   Mon Jan 24 11:02 - 16:04 (4+05:01)
reboot    ~                         Mon Jan 24 11:01 
shutdown  ~                         Mon Jan 24 11:01 
panman82  ttys000                   Mon Jan 24 08:38 - 08:48  (00:09)
panman82  console                   Mon Jan 24 08:12 - 11:01  (02:49)
reboot    ~                         Mon Jan 24 08:08 
shutdown  ~                         Fri Jan 21 16:06 
panman82  console                   Tue Jan 18 10:26 - 16:06 (3+05:39)
reboot    ~                         Tue Jan 18 10:25 
shutdown  ~                         Fri Jan 14 16:03 
panman82  console                   Thu Jan 13 14:21 - 16:03 (1+01:42)
reboot    ~                         Thu Jan 13 14:17 
shutdown  ~                         Thu Jan 13 14:17 
panman82  ttys000                   Wed Jan 12 13:34 - 13:58 (1+00:24)
panman82  console                   Mon Jan 10 09:11 - 14:17 (3+05:05)
reboot    ~                         Mon Jan 10 09:10 
shutdown  ~                         Fri Jan  7 16:02 
panman82  console                   Fri Jan  7 14:26 - 16:02  (01:36)
reboot    ~                         Fri Jan  7 14:24 
reboot    ~                         Fri Jan  7 14:24 
root      console                   Fri Jan  7 14:20 - crash  (00:03)
panman82  console                   Tue Jan  4 07:30 - 14:20 (3+06:50)
reboot    ~                         Tue Jan  4 07:28 
shutdown  ~                         Mon Jan  3 16:03 
panman82  ttys000                   Mon Jan  3 13:37 - 15:48  (02:11)
panman82  ttys000                   Mon Jan  3 13:36 - 13:36  (00:00)
other.admin   ttys000  10.3.33.20       Mon Jan  3 10:08 - 10:32  (00:23)
panman82  ttys000                   Mon Jan  3 09:38 - 09:39  (00:01)
panman82  console                   Mon Jan  3 09:13 - 16:03  (06:49)
reboot    ~                         Mon Jan  3 09:11 
shutdown  ~                         Mon Jan  3 09:11 
panman82  console                   Mon Jan  3 08:38 - 09:11  (00:32)
reboot    ~                         Mon Jan  3 08:35 
shutdown  ~                         Thu Dec 23 15:12 
other.admin   ttys000  10.3.33.39       Thu Dec 23 15:03 - 15:11  (00:08)
panman82  ttys000                   Thu Dec 23 09:47 - 10:16  (00:29)
panman82  ttys000                   Thu Dec 23 09:30 - 09:47  (00:16)
panman82  console                   Thu Dec 23 07:52 - 15:12  (07:19)
reboot    ~                         Thu Dec 23 07:50 
shutdown  ~                         Wed Dec 22 15:57 
panman82  console                   Wed Dec 22 15:54 - 15:57  (00:02)
reboot    ~                         Wed Dec 22 15:53 
shutdown  ~                         Wed Dec 22 15:53 
panman82  ttys000                   Wed Dec 22 13:51 - 14:04  (00:12)
panman82  ttys000                   Wed Dec 22 12:47 - 13:40  (00:53)
panman82  console                   Mon Dec 20 10:57 - 15:53 (2+04:55)
reboot    ~                         Mon Dec 20 10:55 
shutdown  ~                         Fri Dec 17 16:15 
panman82  ttys000                   Fri Dec 17 14:41 - 14:43  (00:02)
panman82  console                   Wed Dec 15 07:39 - 16:15 (2+08:36)
reboot    ~                         Wed Dec 15 07:38 
shutdown  ~                         Tue Dec 14 16:01 
panman82  console                   Tue Dec 14 13:43 - 16:01  (02:18)
reboot    ~                         Tue Dec 14 13:41 
shutdown  ~                         Tue Dec 14 13:40 
panman82  console                   Mon Dec 13 08:16 - 13:40 (1+05:24)
reboot    ~                         Mon Dec 13 08:14 
shutdown  ~                         Mon Dec 13 08:14 
panman82  console                   Mon Dec 13 07:36 - 08:14  (00:37)
reboot    ~                         Mon Dec 13 07:35 
shutdown  ~                         Fri Dec 10 16:09 
panman82  console                   Fri Dec 10 14:46 - 16:09  (01:23)
reboot    ~                         Fri Dec 10 14:45 
shutdown  ~                         Fri Dec 10 14:45 
panman82  ttys000                   Wed Dec  8 15:36 - 16:09  (00:33)
panman82  ttys000                   Wed Dec  8 15:12 - 15:25  (00:12)
panman82  ttys000                   Wed Dec  8 14:27 - 14:30  (00:02)
panman82  ttys000                   Mon Dec  6 11:44 - 11:45  (00:01)
panman82  console                   Mon Dec  6 07:31 - 14:45 (4+07:13)
reboot    ~                         Mon Dec  6 07:30 
shutdown  ~                         Fri Dec  3 15:57 
panman82  console                   Fri Dec  3 13:27 - 15:57  (02:29)
reboot    ~                         Fri Dec  3 13:24 
reboot    ~                         Fri Dec  3 13:24 
root      console                   Fri Dec  3 13:13 - crash  (00:10)
panman82  console                   Thu Dec  2 07:31 - 13:13 (1+05:42)
reboot    ~                         Thu Dec  2 07:30 
shutdown  ~                         Wed Dec  1 16:04 
panman82  console                   Mon Nov 29 07:32 - 16:04 (2+08:31)
reboot    ~                         Mon Nov 29 07:30 
shutdown  ~                         Wed Nov 24 15:35 
panman82  console                   Wed Nov 24 11:48 - 15:35  (03:46)
panman82  console                   Wed Nov 24 10:54 - 11:48  (00:54)
reboot    ~                         Wed Nov 24 10:52 
shutdown  ~                         Wed Nov 24 10:51 
panman82  console                   Mon Nov 22 07:36 - 10:51 (2+03:15)
reboot    ~                         Mon Nov 22 07:34 
shutdown  ~                         Fri Nov 19 15:22 
panman82  console                   Wed Nov 17 07:03 - 15:22 (2+08:18)
reboot    ~                         Wed Nov 17 07:02 
shutdown  ~                         Tue Nov 16 16:09 
panman82  console                   Tue Nov 16 13:17 - 16:09  (02:52)
panman82  console                   Tue Nov 16 09:42 - 13:16  (03:34)
panman82  console                   Tue Nov 16 08:07 - 09:41  (01:33)
reboot    ~                         Tue Nov 16 08:06 
shutdown  ~                         Mon Nov 15 16:41 
panman82  console                   Mon Nov 15 11:58 - 16:41  (04:42)
reboot    ~                         Mon Nov 15 11:55 
shutdown  ~                         Mon Nov  1 10:11 
other.admin   console                   Thu Oct 28 11:47 - 10:10 (3+22:23)
reboot    ~                         Thu Oct 28 11:45 
other.admin   console                   Thu Oct 28 09:39 - crash  (02:06)
reboot    ~                         Thu Oct 28 09:38 
shutdown  ~                         Mon Oct 25 15:32 
student   console                   Mon Oct 25 09:40 - 15:32  (05:51)
student   console                   Mon Oct 25 09:36 - 09:40  (00:03)
student   console                   Mon Oct 25 09:33 - 09:36  (00:02)
student   console                   Mon Oct 25 08:07 - 09:33  (01:26)
student   console                   Mon Oct 25 08:04 - 08:06  (00:02)
other.user  console                   Mon Oct 25 08:04 - 08:04  (00:00)
student   console                   Mon Oct 25 08:00 - 08:04  (00:03)
reboot    ~                         Mon Oct 25 07:57 
shutdown  ~                         Fri Oct 22 14:17 
student   console                   Fri Oct 22 14:14 - 14:17  (00:03)
reboot    ~                         Fri Oct 22 14:13 
shutdown  ~                         Wed Oct 20 15:43 
other.admin   ttys000                   Wed Oct 20 14:42 - 14:46  (00:03)
other.admin   console                   Wed Oct 20 14:40 - 15:43  (01:03)
tech-user  console                   Wed Oct 20 13:07 - 14:14  (01:06)
other.user  console                   Wed Oct 20 13:05 - 13:07  (00:01)
student   console                   Wed Oct 20 13:02 - 13:05  (00:03)
other.user  console                   Wed Oct 20 07:55 - 13:01  (05:05)
other.admin   ttys000  other.computer.com Wed Oct 20 07:53 - 07:54  (00:00)
reboot    ~                         Wed Oct 20 07:52 
shutdown  ~                         Tue Oct 19 13:24 
other.admin   console                   Tue Oct 19 12:13 - 13:24  (01:11)
student   console                   Tue Oct 19 11:10 - 12:13  (01:03)
other.admin   ttys000  other.computer.com Tue Oct 19 11:07 - 11:09  (00:02)
other.user  console                   Tue Oct 19 09:14 - 11:06  (01:51)
reboot    ~                         Tue Oct 19 09:13 
shutdown  ~                         Fri Oct 15 14:29 
other.admin   console                   Fri Oct 15 11:13 - 14:29  (03:15)
reboot    ~                         Fri Oct 15 11:12 
shutdown  ~                         Mon Oct 11 15:49 
student   console                   Mon Oct 11 15:29 - 15:49  (00:19)
student   console                   Mon Oct 11 15:27 - 15:29  (00:01)
reboot    ~                         Mon Oct 11 15:26 
shutdown  ~                         Mon Oct 11 13:14 
student   console                   Mon Oct 11 09:17 - 13:14  (03:57)
reboot    ~                         Mon Oct 11 09:16 
shutdown  ~                         Fri Oct  8 15:03 
student   console                   Fri Oct  8 14:21 - 15:03  (00:41)
student   console                   Fri Oct  8 12:35 - 14:21  (01:46)
other.admin   console                   Fri Oct  8 12:33 - 12:34  (00:00)
student   console                   Wed Oct  6 10:50 - 12:32 (2+01:42)
student   console                   Wed Oct  6 10:49 - 10:50  (00:01)
other.admin   console                   Wed Oct  6 10:43 - 10:48  (00:05)
other.user  console                   Wed Oct  6 10:41 - 10:43  (00:01)
student   console                   Wed Oct  6 10:40 - 10:41  (00:00)
student   console                   Wed Oct  6 10:38 - 10:40  (00:01)
student   console                   Wed Oct  6 10:33 - 10:38  (00:04)
reboot    ~                         Wed Oct  6 10:32 
shutdown  ~                         Mon Oct  4 09:39 
student   console                   Thu Sep 30 10:47 - 09:39 (3+22:52)
other.admin   ttys000  other.computer.com Thu Sep 30 10:43 - 10:43  (00:00)
other.admin   console                   Thu Sep 30 10:36 - 10:39  (00:03)
reboot    ~                         Thu Sep 30 10:35 
shutdown  ~                         Wed Sep 29 16:31 
reboot    ~                         Wed Sep 29 16:27 
shutdown  ~                         Wed Sep 29 16:27 
reboot    ~                         Wed Sep 29 16:14 
shutdown  ~                         Mon Jul 19 10:05 
other.admin   console                   Mon Jul 19 09:53 - 10:05  (00:11)
reboot    ~                         Mon Jul 19 09:51 
shutdown  ~                         Mon Jul 19 09:51 
other.admin   console                   Mon Jul 19 09:38 - 09:51  (00:13)
reboot    ~                         Mon Jul 19 09:37 
reboot    ~                         Mon Jul 19 09:36 
root      console                   Mon Jul 19 09:31 - crash  (00:05)
other.admin   console                   Mon Jul 19 09:23 - 09:31  (00:08)
root      console                   Mon Jul 19 09:19 - 09:23  (00:04)
reboot    ~                         Mon Jul 19 09:18 

---------- Post updated at 11:23 AM ---------- Previous update was at 11:17 AM ----------

Also, I modified the original PHP script above and made it work directly as a script. This does work for me but would like to convert it to Bash if possible... Thanks again for any input! Even if it's "not possible".

#!/usr/bin/php
<?php

// Get raw output from Bash
$sessions = shell_exec('last | grep console | awk \'{print $1 " " $9}\'');

// Separate each line into sessions
$sessions = explode(PHP_EOL, $sessions);

// Remove last line if empty
if (end($sessions) == '') {
    array_pop($sessions);
}

// Separate each session from the user and time
array_walk($sessions, create_function('&$e,$k', '$e=explode(" ", $e);'));

// Placeholder for user => logged-in times
$login_times = array();

// Loop through each session
foreach ($sessions as $session) {

    // List the session details
    list($user, $time) = $session;

    // Ignore poorly formatted times
    // Else parse the time to get d+hh:mm
    if (!preg_match('/^\(((\d+)\+)?(\d{2})\:(\d{2})\)$/', $time, $matches)) {
        continue;
    }

    // Convert to minutes
    $time  =  (int) $matches[4];
    $time += ((int) $matches[3] * 60);
    $time += ((int) $matches[2] * 24 * 60);

    // Add users session time to the total logged-in time
    @$login_times[$user] += $time;
}

// Sort by logged-in time
arsort($login_times, SORT_NUMERIC);

// Get top most user
echo '<result>' . key($login_times) . '</result>' . PHP_EOL;

Okay, this works for me when I feed it your last output on stdin:

#!/bin/bash

while read USER TERM WKDAY MON DAY TIME DASH END DURATION
do
        [ "$TERM" == "console" ] || continue
        [ "$DASH" == "-" ] || continue
        [ "${DURATION:0:1}" == "(" ] || continue

        # Remove brackets
        DURATION="${DURATION:1:$((${#DURATION}-2))}"
        # Strip +07 garbage
        DURATION="${DURATION//+[0-9][0-9]/}"

        # Split 05:30 into 05 and 30
        IFS=":" read HOUR MIN <<< "$DURATION"

        # Strip leading zeroes, otherwise BASH will assume the
        # numbers are octal!
        HOUR="${HOUR#0}"
        MIN="${MIN#0}"

        ((MIN+=(HOURS*60)))

        echo $USER $MIN
done | awk '{ t[$1]+=$2;        }
        END {   most=""
                for(key in t)
                        if(t[most] < t[key])    most=key;

                printf("%s\t%02d:%02d\n", most, t[most]/60, t[most]%60);
        }'
$ ./last.sh < lastdata
panman82        16:09
$

A pure awk solution might be more elegant but the necessary string operations seem much easier in the shell.

Thanks for the script. Looking more into awk, I think you are right that it should be able to do everything. I'll do some more digging there. And I realized that the input I posted is actually wrong, using `last | awk '/console/{print $1 " " $NF}'` would just output something like:

panman82 (1+01:42)
panman82 (4+05:01)
other.user (06:49)
other.admin (2+04:55)
panman82 (01:37)
panman82 (07:18)
panman82 (2+08:31)
other.user (03:57)
other.user (00:03)
other.user (01:06)
panman82 (11+07:43)
last| awk 'NF>6&&!/logged in/ { gsub(/\(|\)/,"") 
                              split($NF,a,"[+:]")
                              b[$1]+=($NF~/+/)?a[1]*3600+a[2]*60+a[3]:a[1]*60+a[2]
                             }
          END{for (i in b) print b, i|"sort -rn|head -1" }' 

160949 panman82

work smarter not harder. man ac

$ whatis ac
ac (1)               - print statistics about users' connect time

ac is not a general command, I can find the command in AIX, but not in Solaris.

Funny I needed to escape the + i.e. ($NF~/\+/) or I got:

awk: 0602-521 There is a regular expression error.
        ?*+ not preceded by valid expression.

see relevant process accounting command for Solaris. not sure what that would be off hand. man acct would be a good start.

That is a nice command for totals, but it also includes terminal time. I'm just looking for console time. Although, if ac was what I needed, this would be a good awk:

ac -p | awk '
BEGIN {
    username = "none";
    total = 0;
}
$1 !~ /total/ {
    if ($NF > total) {
        username = $1;
        total = $NF;
    }
}
END {
    print username;
}
'

---------- Post updated at 02:02 PM ---------- Previous update was at 01:07 PM ----------

Nice, very close to what I'm looking for. Before you posted yesterday I came up with something similar, using match() instead of split(). With a few modifications to what you came up with, and parts of what I came up with, this is the result:

last | awk '
$2 ~ /console/ {
    gsub(/\(|\)/, "", $NF);
    split($NF, times, "[+:]");
    if ($NF ~ /\+/) {
        logins[$1] += times[1] * 1440;
        logins[$1] += times[2] * 60;
        logins[$1] += times[3];
    } else {
        logins[$1] += times[1] * 60;
        logins[$1] += times[2];
    }
}
END {
    username = "none";
    longest = 0;
    for (user in logins) {
        if (logins[user] > longest) {
            username = user;
            longest = logins[user];
        }
    }
    print username;
}
'

Any optimizations or even better ways of doing this? First thought is the END statement, doesn't seem to be a good way to sort the array (on Mac OS X) and get the top/bottom result. Also, I think your day's calculation was wrong, 24 hours in a day * 60 minutes in an hour = 1440. No?

Yes, should be 1400, here is the fix, you can get the username directly.

awk '$2 ~ /console/ { gsub(/\(|\)/,""); split($NF,a,"[+:]")
                      b[$1]+=($NF~/+/)?a[1]*1400+a[2]*60+a[3]:a[1]*60+a[2]
                      if (max<b[$1]) {max=b[$1];name=$1}
                    }END{print name}' infile