Linux - NewbieThis Linux forum is for members that are new to Linux.
Just starting out and have a question?
If it is not in the man pages or the how-to's this is the place!
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
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.
Pick your language eg shell, Perl, awk etc, then decide how far back you want to go, then do a trial run with just a few records for test purposes.
Note you'll want to skip no-user entries eg reboot.
What are you going to do if a user has been deleted from the system; will you still count those recs? If not, x-ref against passwd file; might be a good way to skip non-user recs anyway.
Have a go and come back with specific qns and show us your code when you do.
Personally I'd take a copy of the last cmd output rather than pipe it direct into the prog, but that's your call.
Something like this, maybe? At the mention perl I couldn't resist ;}
Code:
#!/usr/bin/perl
#===============================================================================
#
# FILE: last.pl
#
# USAGE: last | ./last.pl
#
# DESCRIPTION: Getting the accumulated login times of users
#===============================================================================
use strict;
use warnings;
my %hours;
my %minutes;
my %logins;
my $user;
while (<STDIN>) { # While we have input...
# Find lines and save username, login time
if ( /^(\S+).*\((.*)\)/ && !/^reboot/ ) {
my $hours;
my $minutes;
# jump through a hoop if the user was on for longer than 24 hours
# in one go ... :)
my @tmp=split(/:|\+/, $2 );
# Increment total hours, minutes, and logins
if( $#tmp < 2){
# no overnighters
$hours{$1} += $tmp[0];
$minutes{$1} += $tmp[1]
}else{
# overnighters
$hours{$1} += $tmp[0]*24 +$tmp[1];
$minutes{$1} += $tmp[2]
}
$logins{$1}++;
}
}
# For each user in the array...
foreach $user (sort(keys %hours)) {
# Calculate hours from total minutes
$hours{$user} += int($minutes{$user} / 60);
$minutes{$user} %= 60;
# Print the information for this user
print "User $user, total login time ";
printf "%02d:%02d, ", $hours{$user}, $minutes{$user};
print "total logins $logins{$user}.\n";
}
# Grab a point in time snapshot of the output from the last command
last > /tmp/last_output
# Alternatively because there are output variances to deal with,
last | grep '+' > /tmp/day_more && last | grep -v '+' > /tmp/day_less
# Now we have complete output of last, and split versions of sessions
# that are shorter or longer than one day. We should convert days in
# the day_more file to hours and append to day_less for consistency.
# placeholder to code this thought process...
# we are not interested in 00 minute entries (unless we also decide to count logins)
# We are not interested in any users that are currently logged in or the wtmp footer
# We are not interested in system entries either. Let's hope no users match our egrep
egrep -v '(00:00|logged|^$|wtmp|system)' /tmp/last_output | sed 's/[()]//g' > /tmp/last_clean
cut -d' ' -f1 /tmp/last_clean | sort -u > /tmp/unique_users
# We can crunch data (output) in several ways; Read a line, test each field, process...
# Or we can break our data into separate outputs (by user) before crunching the data.
for user in $(cat /tmp/unique_users)
do
# Guarantee we are starting each loop with clean variables
minute=0
tm=0
hour=0
th=0
day=0
td=0
for time in $(awk /^${user}/'{print $NF}' /tmp/last_clean)
do
temp=$(echo ${time} | cut -d+ -f1)
if [ ${time} = ${temp} ]; then # The day indicator (+) does not exist
day=0
hour=$(echo ${time} | cut -d: -f1 | sed 's/^0//')
else
day=$(echo ${time} | cut -d+ -f1)
hour=$(echo ${time} | cut -d+ -f2 | cut -d: -f1 | sed 's/^0//')
fi
# We use sed to strip padded 0's in single digit values for math (07 becomes just 7)
minute=$(echo ${time} | cut -d: -f2 | sed 's/^0//')
((td = td + day))
((th = th + hour))
((tm = tm + minute))
done
# We have our column totals, but now we need to normalize the output again
minute=$(expr $tm % 60)
hour=$(expr `echo "$tm / 60 + $th" | bc` % 24)
day=$(expr `echo "$tm / 60 + $th" | bc` / 24 + $td)
printf "%8s %4d + %02d : %02d \n" $user $day $hour $minute
done
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.