Know how many users have connected to my computer

2019-03-03 05:47发布

I need a script which shows a summary of which users have connected to my computer during the last week and how often.

I know I can use last and filter the time columns with awk, but how? I would have to get each user connected in the last week and calculate the number of connections plus the total time of all connections. This is what I have come up with so far:

for USER in `last | awk '{print $1}' | sort -u`; do
    echo "Conexiones de $USER:"
    last | grep $USER | wc -l
    # BUT I NEED T COUNT ONLY LAST WEEK AND PRINT TOTAL TIME
done

标签: linux bash shell
2条回答
戒情不戒烟
2楼-- · 2019-03-03 06:21

Try this

Steps

  1. last > login.txt

  2. last | awk '{print $3}' | sort -u > names.txt

  3. Iterate each lines from names.txt and apply grep "line1" login.txt | wc -l

you will get count for each user (each user is each line in names.txt)

查看更多
Juvenile、少年°
3楼-- · 2019-03-03 06:26

I strongly advise against parsing the output of last, as its output may differ from implementation to implementation and parsing the login/logout dates is prone to error. Also, it seems that nearly all implementations don't support -F or similar which without you are completely out of luck, as you need the year information. In theory you could check if there is a leap from one month to another one that is more recent on two consecutive lines (e.g. Jan->Dec would indicate a year change), but this heuristic is flawed - you just cannot guess the correct year(s) correctly. For example, take the rare case that nobody logged in for a year.

If you absolutely have to/want to parse its output either way, don't do it with just bash/awk/cut/... for the reasons above. To get the session duration you would either have to parse the prettyprinted login/logout dates yourself or the already calculated duration, which is also prettyprinted and probably varies from implementation to implementation (as in, it's not just hours and minutes. How do get days/weeks/years represented in that column?). Doing this with just bash/awk would be a nightmare and even more prone to breakage than my script below - please don't do it.

The best and least hacky solution would involve writing a small C program or script that operates on the wtmp data directly (man wtmp), but then you would have to calculate the session durations yourself based on login/logout pairs (you don't get this for free; login is one record, logout is a second one). See busybox' last implementation for a reference on how it reads its stuff. This is the way to go if you want to do it the right way.

That being said, I came up with the quick'n'dirty (perl) solution below. It doesn't run the last command, you have to feed it proper input yourself, otherwise it will explode. If your last output looks different than mine, doesn't support -F or Date::Parse cannot parse the format your last command prints, it will also explode. There is lots of room for improvement, but this should get you started.

Notes

  • -F is required for last to print full dates (we need this to get the year, otherwise we cannot determine proper timestamps from its output)
  • -i tells last to output IP addresses, which just makes its output easier to parse
  • it does not parse the session duration column but rather both login/logout dates, converts them to epoch time and calculates the diff to get the session duration. There is no other magic involved in parsing the dates other than using Date::Parse, which means that it has to exclude all sessions that don't have a proper login/logout date (i.e., they are still logged in or their session got terminated due to a reboot, crash, etc.), so these sessions won't be part of the calculated output!
  • it defaults to 7 days, but you can change this on the command line with the -d switch

Code

#!/usr/bin/perl

use strict;
use warnings;

use Date::Parse;
use Getopt::Std;

our $opt_d;
getopt('d');
my $days = $opt_d || 7;
my $since = time() - (60 * 60 * 24 * $days);

my %data;
while (<>)
{
    chomp;
    next if /ssh|reboot|down|crash|still logged in/;
    # last -Fi gives this on my box
    # username   line        ip   Mon Apr  1 18:17:49 2013 - Tue Apr  2 01:00:45 2013  (06:42)
    my ($user, undef, undef, $date_from, $date_to) = /^(\S+)\s+(\S+)\s+([0-9.]+)\s+([[:alnum:]:\s]+)\s+-\s+([[:alnum:]:\s]+[^\s])\s+\(.+\)/;

    my $time_from = str2time($date_from);
    last if $time_from < $since;
    my $time_to   = str2time($date_to);

    $data{$user}{"count"}++;
    $data{$user}{"duration"} += $time_to - $time_from;

    # print "$user|$line|$ip|$date_from|$date_to\n";
}

print "login history for the last $days day(s):\n\n";

if (keys %data > 0)
{
    foreach my $user (keys %data)
    {
        my $duration = $data{$user}{"duration"};
        printf "%s was logged in %d time(s) for a total of %d day(s), %d hour(s) and %d minute(s)\n",
            $user,
            $data{$user}{"count"},
            ($duration / (24 * 60 * 60)),
            ($duration / (60 * 60 )) % 24,
            ($duration / 60 ) % 60,
    }
}
else
{
    print "no logins during the specified time period\n";
}

Example usage

$ last -Fi | ./last_parse.pl -d 700
login history for the last 700 day(s):

root was logged in 25 time(s) for a total of 36 day(s), 12 hour(s) and 35 minute(s)
foobar was logged in 362 time(s) for a total of 146 day(s), 17 hour(s) and 17 minute(s)
quux was logged in 3 time(s) for a total of 0 day(s), 0 hour(s) and 4 minute(s)
$
查看更多
登录 后发表回答