Thread: total login time of each user in last

  1. #1
    Registered User
    Join Date
    Aug 2009
    Posts
    39

    total login time of each user in last

    Hi everyone. I would like to write a script in bash that displays the total login time of every single user shown by unix "last" command, regardless of any different machines he connects from. How can I achieve that? Thank you.

  2. #2
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Generally, I would suggest you pipe the output through awk, and have it print whatever field(s) you want. Unfortunately, this doesn't work well because tthe last utility has a lot of variance in it's output, and everything seems to be space separated, not tab separated. For example, the reboot pseudo user has a tty entry of "system boot", so it's two words, which will screw up the column counts for awk. Same goes for the users that are "still logged in". You can try using the cut program to cut out the first however many characters. Something like
    Code:
    last | cut -c65-
    That will give you everything from the 65th character to the end. This isn't perfect, but on my system, I seem to always get sensible output, though I get "logged in" without the "still" part, if that's okay. I do, however, get leading white space on most time entries, which may be undesirable. You can clean that up easily enough by piping the result through sed, awk, cut and probably some other tools too.

  3. #3
    UT2004 Addict Kleid-0's Avatar
    Join Date
    Dec 2004
    Posts
    656
    Quote Originally Posted by vril View Post
    I would like to write a script in bash that displays the total login time of every single user shown by unix "last" command, regardless of any different machines he connects from. How can I achieve that?
    Code:
    # For user 'corey', give me how many minutes he has been logged in with:
    total_minutes=0; for i in `last | grep corey | egrep -o '\([0-9]{2}:[0-9]{2}\)'` ; do i=`echo $i | egrep -o '[0-9]{2}:[0-9]{2}'` ; minutes=${i##*:}; hours=${i%%:*} ; minutes=$(echo $minutes | sed 's/^0//') ; hours=$(echo $hours | sed 's/^0//') ; let 'total_minutes+=hours*60+minutes' ; done ; echo $total_minutes
    Hope that helps.

  4. #4
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    Nice job. I've simplified it a little. I'm assuming that the sed calls are unnecessary (the leading 0's don't matter). And I replaced the second egrep with another parameter expansion.
    Code:
    total_minutes=0
    for i in `last | grep corey | grep -Eo '\([0-9]{2}:[0-9]{2}\)'` ; do
      minutes=${i##(*:} ; minutes=${minutes%%)}
      hours=${i%%:*)}   ; hours=${hours##(}
      let 'total_minutes+=hours*60+minutes'
    done
    echo $total_minutes
    The two greps could probably be combined by using a sed or awk call instead, but I'm just learning this stuff so I'm not sure.
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  5. #5
    Registered User
    Join Date
    Aug 2009
    Posts
    39
    Quote Originally Posted by Kleid-0 View Post
    Code:
    # For user 'corey', give me how many minutes he has been logged in with:
    total_minutes=0; for i in `last | grep corey | egrep -o '\([0-9]{2}:[0-9]{2}\)'` ; do i=`echo $i | egrep -o '[0-9]{2}:[0-9]{2}'` ; minutes=${i##*:}; hours=${i%%:*} ; minutes=$(echo $minutes | sed 's/^0//') ; hours=$(echo $hours | sed 's/^0//') ; let 'total_minutes+=hours*60+minutes' ; done ; echo $total_minutes
    Hope that helps.
    Thanks, but I need to find the total login time of every single user displayed by "last", not of particular one.

  6. #6
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    Will a perl script do?
    Code:
    #!/usr/bin/perl
    open LAST, "last |";
    while (<LAST>) {
        if (($name,$days,$hours,$mins) = /^(\w+).+\((?:(\d+)\+)?(\d+):(\d+)/) {
            $TIMES{$name} += 1440 * $days + 60 * $hours + $mins;
        }
    }
    foreach (sort keys %TIMES) {
        print "$_ $TIMES{$_}\n";
    }
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  7. #7
    Registered User
    Join Date
    Aug 2009
    Posts
    39
    No, it has to be in bash. Thanks anyway.

  8. #8
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    Then call the perl program from a bash script.
    I mean, if you're allowed to call awk from the bash script then why not perl?
    Or are you not allowed to call awk? What about sed? Or grep?
    awk would probably be a fine choice but I don't know it very well at all (I started using perl on windows about a year ago but am just now learning "unix").

    At any rate, this is starting to sound like a homework assignment and you've been given more than enough hints to do it yourself. Although I should point out that I was wrong in post#4 about the leading zeroes not mattering (I believe it makes bash think the numbers are in octal). The revised script is
    Code:
    total_minutes=0
    for i in `last | grep corey | grep -Eo '\([0-9]{2}:[0-9]{2}\)'` ; do
      mins=${i##(*:}  ; mins=${mins%%)}   ; mins${mins##0}
      hours=${i%%:*)} ; hours=${hours##(} ; hours=${hours##0}
      let 'total_minutes+=hours*60+mins'
    done
    echo $total_minutes
    A problem with the above (and Kleid's code it was based on) is that it ignores the possibility of a time like 1+02:13 which means 1 day, 2 hours, 13 minutes. The perl script in my previous post handles that possibility.

    As another hint, this command will give you a sorted list of the unique names:
    Code:
    last | awk '{print $1}' | sort | uniq`
    You could use that in an outer loop to feed names to the above command.
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  9. #9
    Registered User
    Join Date
    Aug 2009
    Posts
    39
    Thanks a lot! Problem solved! Now I would like to deal with another problem if I don't bother. How could I show the number of sessions for the past 10 days? An approximation would be, at first, to sort uniquely the output of last by the field of day number, but there is a complication when we go back up to the previous month. Any ideas with that?

  10. #10
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    Quote Originally Posted by vril View Post
    Thanks a lot! Problem solved!
    Could you post the solution please?

    Quote Originally Posted by vril View Post
    Now I would like to deal with another problem if I don't bother. How could I show the number of sessions for the past 10 days? An approximation would be, at first, to sort uniquely the output of last by the field of day number, but there is a complication when we go back up to the previous month. Any ideas with that?
    What ideas do you have?
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  11. #11
    UT2004 Addict Kleid-0's Avatar
    Join Date
    Dec 2004
    Posts
    656
    Quote Originally Posted by vril View Post
    An approximation would be, at first, to sort uniquely the output of last by the field of day number, but there is a complication when we go back up to the previous month...
    What is the "complication"?

  12. #12
    Registered User
    Join Date
    Aug 2009
    Posts
    39
    Quote Originally Posted by oogabooga View Post
    Could you post the solution please?
    Code:
    	users="$( last | grep -v "wtmp" | awk '{print $1}' | sort -u )"
    		for user in $users; do
    			totmin=0
    			qq=$( echo "$res" | grep "$user" | grep -v "still logged in" | awk '{print $NF}' | sed 's/[()]//g' ) 
    			for q in $qq; do
    				min="$( echo $q | awk -F: -v hrs=0 -v min=0 	'{hrs=hrs+$1; min=min+$2}END{min=hrs*60+min;print min}' )"
    				echo $min
    				totmin=$(( $totmin + $min ))
    			done
    			qq=$( echo "$res" | grep "$user" | grep "still logged in" | awk '{print $NF}'
    			if [ -n qq ]; then
    				for q in $qq; do
    					min="$( echo $q | awk -F: -v hrs=0 -v min=0 '{hrs=hrs+$1; min=min+$2}END{min=hrs*60+min;print min}' )"
    					hrs=$( date -u +%H )
    					mins=$( date -u +%M )
    					mins=$(( $(($hrs * 60))  + $mins ))
    					min=$(( $mins - $min ))
    					totmin=$(( $totmin + $min ))
    				done
    			fi
    			echo "$user	$totminmins" >> rep1.txt
    Sorry for not following your suggestion, but meanwhile I had an inspiration. As you see, I haven't coped with the possibity there are days in time, yet.
    What ideas do you have?
    I haven't . This is why I am asking.

  13. #13
    Registered User
    Join Date
    Aug 2009
    Posts
    39
    Quote Originally Posted by Kleid-0 View Post
    What is the "complication"?
    Let's say there are the dates " April 1 and March 31". If I use sort in day number field to hold only one occurence of each date, March 31 will be put prior to April 1, which is wrong.

  14. #14
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    Quote Originally Posted by vril View Post
    Code:
    	users="$( last | grep -v "wtmp" | awk '{print $1}' | sort -u )"
    		for user in $users; do
    			totmin=0
    			qq=$( echo "$res" | grep "$user" | grep -v "still logged in" | awk '{print $NF}' | sed 's/[()]//g' ) 
    			for q in $qq; do
    				min="$( echo $q | awk -F: -v hrs=0 -v min=0 	'{hrs=hrs+$1; min=min+$2}END{min=hrs*60+min;print min}' )"
    				echo $min
    				totmin=$(( $totmin + $min ))
    			done
    			qq=$( echo "$res" | grep "$user" | grep "still logged in" | awk '{print $NF}'
    			if [ -n qq ]; then
    				for q in $qq; do
    					min="$( echo $q | awk -F: -v hrs=0 -v min=0 '{hrs=hrs+$1; min=min+$2}END{min=hrs*60+min;print min}' )"
    					hrs=$( date -u +%H )
    					mins=$( date -u +%M )
    					mins=$(( $(($hrs * 60))  + $mins ))
    					min=$(( $mins - $min ))
    					totmin=$(( $totmin + $min ))
    				done
    			fi
    			echo "$user	$totminmins" >> rep1.txt
    Clearly I'm missing something here, but what is $res? Where is it set? Have you posted the entire script?
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  15. #15
    Registered User
    Join Date
    Aug 2009
    Posts
    39
    Sorry, you are right. $res holds the output of "last".
    Code:
    res=$( last )
    Anyway, let's focus on the other problem now.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. need help with Tracking user login
    By erictu in forum C++ Programming
    Replies: 25
    Last Post: 04-01-2012, 06:57 PM
  2. User login history - bash
    By garcy in forum Linux Programming
    Replies: 3
    Last Post: 05-17-2011, 10:23 AM
  3. Replies: 6
    Last Post: 01-08-2006, 02:49 PM
  4. Replies: 5
    Last Post: 05-06-2004, 09:14 AM
  5. creating a user login system for web pages
    By Nutshell in forum A Brief History of Cprogramming.com
    Replies: 1
    Last Post: 07-04-2002, 11:02 PM