![Berechnen Sie die Anmeldedauer eines Benutzers in Minuten aus einer Datei](https://rvso.com/image/192212/Berechnen%20Sie%20die%20Anmeldedauer%20eines%20Benutzers%20in%20Minuten%20aus%20einer%20Datei.png)
Ich habe eine Datei auf einem Linux-System, die wie folgt aussieht:
May 6 19:12:03 sys-login: user1 172.16.2.102 Login /data/netlogon 13473
May 6 19:15:26 sys-login: user2 172.16.2.107 Login /data/netlogon 14195
May 6 19:28:37 sys-logout: user1 172.16.2.102 Logout /data/netlogon 13473
May 6 19:33:28 sys-logout: user2 172.16.2.107 Logout /data/netlogon 14195
May 8 07:58:50 sys-login: user3 172.16.6.128 Login /data/netlogon 13272
May 8 07:58:50 sys-logout: user3 172.16.6.128 Logout /data/netlogon 13272
Und ich versuche, die Zeit in Minuten zu berechnen, die jeder Benutzer zwischen dem Anmelden und Abmelden verbracht hat. Es wird nur eine Anmeldung/Abmeldung pro Benutzer geben, und ich möchte einen Bericht für alle Benutzer gleichzeitig erstellen.
Was ich versucht habe:
Ich habe zunächst versucht, die Benutzer zu extrahieren:
users=$(awk -v RS=" " '/login/{getline;print $0}' data)
das gibt die Benutzer (angemeldet) zurück, und dann versuche ich, die Zeit zu ermitteln, zu der sie sich angemeldet haben, aber ich stecke derzeit fest. Für jede Hilfe wäre ich dankbar!
Bearbeiten: Ich kann Benutzer und Daten wie folgt abrufen:
users=$(grep -o 'user[0-9]' data)
dates=$(grep -o '[0-2][0-9]:[0-5][0-9]:[0-5][0-9]' data)
Wenn ich eine vollständige Lösung finde, werde ich sie hier teilen.
Antwort1
Obwohl diese Site „kein Skript-Schreibservice“ ist ;), ist dies eine nette kleine Übung, daher schlage ich das folgende awk
Programm vor. Sie können es in einer Datei speichern calc_logtime.awk
.
#!/usr/bin/awk -f
/sys-log[^:]+:.*Log/ {
user=$5
cmd=sprintf("date -d \"%s %d %s\" \"+%%s\"",$1,$2,$3)
cmd|getline tst
close(cmd)
if ($7=="Login") {
login[user]=tst
}
else if ($7=="Logout") {
logtime[user]+=(tst-login[user])
login[user]=0
}
}
END {
for (u in logtime) {
minutes=logtime[u]/60
printf("%s\t%.1f min\n",u,minutes)
}
}
Dies setzt voraus, dass der GNU- date
Befehl verwendet wird (Teil der Standard-Tool-Suite auf GNU/Linux-Systemen) und dass das Zeitformat in Ihrer Protokolldatei wie angegeben ist. Beachten Sie auch, dass dies nicht viele Sicherheitsprüfungen enthält, aber Sie sollten eine Vorstellung davon bekommen, wie Sie es an Ihre Bedürfnisse anpassen können.
- Es wird nach Zeilen gesucht, die enthaltenbeidedie Zeichenfolge
sys-log
am Anfang undLog
am Ende, um die Selektivität zu erhöhen, falls es noch anderen Inhalt geben könnte. Wie gesagt, dies ist ein sehr rudimentärer Test, aber Sie können sich auch hier ein Bild davon machen, wie man ihn spezifischer gestalten kann. - Der Benutzer wird als 5. durch Leerzeichen getrenntes Feld der Zeile extrahiert.
- Die Aktion wird als 7. durch Leerzeichen getrenntes Feld der Zeile extrahiert.
- Der Zeitstempel der Aktion wird in „Sekunden seit der Epoche“ umgewandelt, indem ein
date
Aufruf über generiertsprintf
und die Aufgabe an die Shell delegiert wird. - Wenn die Aktion ist
Login
, wird der Zeitstempel in einem Array gespeichertlogin
, mit dem Benutzernamen als „Array-Index“. - Wenn die Aktion lautet
Logout
, wird die Dauer berechnet und zu einem Array hinzugefügt,logtime
das die bisherige Gesamtprotokollzeit für alle Benutzer enthält. - Am Ende der Datei wird ein Bericht generiert, indem alle „Array-Indizes“ durchlaufen
logtime
und die Protokollzeiten durch einfache Division von Sekunden in Minuten umgerechnet werden.
Sie erreichen es über
awk -f calc_logtime.awk logfile.dat
Antwort2
Mit GNU awk für Zeitfunktionen und gensub() und Arrays von Arrays:
$ cat tst.awk
BEGIN {
dateFmt = strftime("%Y") " %02d %02d %s"
months = "JanFebMarAprMayJunJulAugSepOctNovDec"
}
{
date = sprintf(dateFmt, (index(months,$1)+2)/3, $2, gensub(/:/," ","g",$3))
userSecs[$5][$7] = mktime(date)
}
$7 == "Logout" {
printf "%s %0.2f\n", $5, (userSecs[$5]["Logout"] - userSecs[$5]["Login"]) / 60
delete userSecs[$5]
}
$ awk -f tst.awk file
user1 16.57
user2 18.03
user3 0.00
Dies läuft um Größenordnungen schneller als der Aufruf von Unix date
aus awk, da letzteres hierfür jedes Mal eine Subshell starten muss.
Wenn Sie beim Ausführen des Skripts auch einen Bericht über die Benutzer erhalten möchten, die sich angemeldet, aber nicht abgemeldet haben, beispielsweise user4
in dieser geänderten Eingabedatei:
$ cat file
May 6 19:12:03 sys-login: user1 172.16.2.102 Login /data/netlogon 13473
May 6 19:15:26 sys-login: user2 172.16.2.107 Login /data/netlogon 14195
May 6 19:28:37 sys-logout: user1 172.16.2.102 Logout /data/netlogon 13473
May 6 19:33:28 sys-logout: user2 172.16.2.107 Logout /data/netlogon 14195
May 8 07:58:50 sys-login: user3 172.16.6.128 Login /data/netlogon 13272
May 8 07:58:50 sys-logout: user3 172.16.6.128 Logout /data/netlogon 13272
Jun 15 08:30:26 sys-login: user4 172.16.2.107 Login /data/netlogon 14195
Dann optimieren Sie einfach das Skript:
$ cat tst.awk
BEGIN {
dateFmt = strftime("%Y") " %02d %02d %s"
months = "JanFebMarAprMayJunJulAugSepOctNovDec"
}
{
date = sprintf(dateFmt, (index(months,$1)+2)/3, $2, gensub(/:/," ","g",$3))
userSecs[$5][$7] = mktime(date)
}
$7 == "Logout" {
printf "%s %0.2f %s\n", $5, (userSecs[$5]["Logout"] - userSecs[$5]["Login"]) / 60, "Complete"
delete userSecs[$5]
}
END {
now = systime()
for (user in userSecs) {
printf "%s %0.2f %s\n", user, (now - userSecs[user]["Login"]) / 60, "Partial"
}
}
$ awk -f tst.awk file
user1 16.57 Complete
user2 18.03 Complete
user3 0.00 Complete
user4 51.10 Partial
Wenn Sie Fälle finden müssten, in denen sich ein Benutzer erneut angemeldet hat, während er bereits angemeldet war, ohne sich zwischendurch abzumelden, oder Abmeldungen ohne zugehörige Anmeldung anders behandeln oder irgendetwas anderes tun müssten, dann wären das alles ebenfalls nur triviale Optimierungen.
Antwort3
Das folgende perl
Skript verwendet dieDatum::ParseModul aus demZeit DatumSammlung, um die Daten und Zeiten aus jedem Datensatz zu analysieren, anstatt sich dabei auf GNU Date zu verlassen. Dies ist wahrscheinlich für Ihre Distribution gepackt (unter Debian, apt install libtimedate-perl
), andernfalls installieren Sie es mit cpan
.
Das Skript funktioniert, indem es das letzte Feld jeder Eingabezeile (das eine Sitzungs-ID zu sein scheint) als Schlüssel der obersten Ebene für eine Hash-of-Hashes (HoH)-Datenstruktur namens verwendet %sessions
. Jedes Element von %sessions ist ein anonymer Hash, der die Schlüssel user
, login
, und enthält logout
.
Sobald die gesamte Datei eingelesen und analysiert wurde, werden die kumulierten Gesamtsummen für jeden Benutzer berechnet (und in einem anderen assoziativen Array gespeichert %users
) und dann gedruckt. Die Ausgabe wird nach Benutzernamen sortiert.
#!/usr/bin/perl -l
use strict;
use Date::Parse;
my %sessions;
my %users;
# read the input file, parse dates, store login and logout times into session hash
while (<>) {
next unless (m/\ssys-log(?:in|out):\s/);
my ($M, $D, $T, $type, $user, $ip, undef, undef, $s) = split;
$type =~ s/^sys-|://g;
$sessions{$s}->{user} = $user;
$sessions{$s}->{$type} = str2time(join(" ", $M, $D, $T));
# $session{$s}->{IP} = $ip; # not used
};
# add up session totals for each user
foreach my $s (keys %sessions) {
# ignore sessions without both a login and logout time, it's
# impossible to calculate session length.
next unless ( defined($sessions{$s}->{login}) &&
defined($sessions{$s}->{logout}) );
$users{$sessions{$s}->{user}} += $sessions{$s}->{logout} - $sessions{$s}->{login};
};
# print them
foreach my $u (sort keys %users) {
printf "%s has logged in for %s minutes\n", $u, int($users{$u}/60);
};
Speichern Sie es beispielsweise unter login-times.pl
und machen Sie es mit ausführbar chmod +x login-times.pl
. Führen Sie es wie folgt aus:
$ ./login-times.pl data
user1 has logged in for 16 minutes
user2 has logged in for 18 minutes
user3 has logged in for 0 minutes
Zu Ihrer Information: Die Daten im %sessions
HoH sehen folgendermaßen aus:
%sessions = {
13272 => { login => 1620424730, logout => 1620424730, user => "user3" },
13473 => { login => 1620292323, logout => 1620293317, user => "user1" },
14195 => { login => 1620292526, logout => 1620293608, user => "user2" },
}
Es ist durchaus möglich, dass eine Sitzung weder einen Anmelde- noch einen Abmeldezeitstempel hat. Es wäre einfach, eine Meldung an STDERR auszugeben, wenn einer von beiden fehlt. Oder Sie können solche Anomalien nach Belieben behandeln. Das obige Skript ignoriert sie einfach.
Der Vollständigkeit halber %users
sehen die Daten am Ende folgendermaßen aus:
%users = { user1 => 994, user2 => 1082, user3 => 0 }
Übrigens wurden diese Datenstrukturen mit demDatenmüllModul, das zum Debuggen usw. sehr nützlich ist. Der Debian-Paketname ist libdata-dump-perl
, andere Distributionen haben es wahrscheinlich. Andernfalls installieren Sie es mit cpan
.
Um diese auszudrucken, habe ich am Ende des Skripts Folgendes hinzugefügt:
use Data::Dump qw(dump);
print "%sessions = ", dump(\%sessions);
print "%users = ", dump(\%users)
Schließlich wird die IP-Adresse mit der Funktion im Skript erfasst, split
aber nicht verwendet. Diese könnte leicht zum Sitzungs-Hash hinzugefügt und verwendet werden, um eine einzeilige Zusammenfassung jedes Login- und Logout-Paares auszudrucken. DieDatumsformatTime::Date
Zum Formatieren der Daten könnte ein Modul aus derselben Sammlung verwendet werden.
Zum Beispiel:
use Date::Format;
nach deruse Date::Parse;
Zeile hinzufügenEntfernen Sie die Kommentierung
$session{$s}->{IP} = $ip;
aus derwhile(<>)
Schleife.Verwenden Sie zum Ausdrucken der Daten beispielsweise Folgendes:
my $tfmt = "%Y-%m-%d %H:%M:%S";
printf "%s\t%-20s\t%-20s\t%7s\t%s\n", "USER", "LOGIN", "LOGOUT", "MINUTES", "IP";
# sort the session keys by their 'user' fields.
foreach my $s (sort { $sessions{$a}->{user} cmp $sessions{$b}->{user} } keys %sessions) {
my $in = $sessions{$s}->{login};
my $out = $sessions{$s}->{logout};
next unless ($in && $out);
my $user = $sessions{$s}->{user};
my $ip = $sessions{$s}->{IP};
my $minutes = int(($out-$in)/60);
$in = time2str($tfmt,$in);
$out = time2str($tfmt,$out);
printf "%s\t%-20s\t%-20s\t%7i\t%s\n", $user, $in, $out, $minutes, $ip;
};
Die Ausgabe sähe etwa so aus:
USER LOGIN LOGOUT MINUTES IP
user1 2021-05-06 19:12:03 2021-05-06 19:28:37 16 172.16.2.102
user2 2021-05-06 19:15:26 2021-05-06 19:33:28 18 172.16.2.107
user3 2021-05-08 07:58:50 2021-05-08 07:58:50 0 172.16.6.128
Antwort4
Das klingt nach einem Job für dateutils
. Fischen Sie die relevanten Teile heraus mit awk
:
awk -v OFS='\t' '
$4 == "sys-login:" { login[$5] = $1" "$2" "$3 }
$4 == "sys-logout:" { logout[$5] = $1" "$2" "$3 }
END {
for (user in login)
print user, login[user], logout[user]
}' infile
Ausgabe:
user1 May 6 19:12:03 May 6 19:28:37
user2 May 6 19:15:26 May 6 19:33:28
user3 May 8 07:58:50 May 8 07:58:50
Und leiten Sie es an eine While-Schleife weiter:
while IFS=$'\t' read username starttime endtime; do
printf "%s\t%s\n" $username \
$(dateutils.ddiff -i "%b %d %H:%M:%S" -f "%S" "$starttime" "$endtime")
done
Ausgabe:
user1 994
user2 1082
user3 0
Hinweis: Sie können den Schalter ddiff
des Befehls ändern -f
, um ein anderes Zeitformat auszuwählen. Hier verwenden wir die verstrichenen Sekunden.