ви
внутри типа vi
:set nowrap
Я полагаю, что есть плагин для vi, который даст вам поведение, похожее на хвост.
Хотя этот сайт "не является службой написания скриптов -" ; ), это небольшое упражнение, поэтому я предлагаю следующую awk
программу. Вы можете сохранить его в файл 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)
}
}
Это зависит от использования команды GNU date
(из стандартного набора инструментов в системах GNU/Linux )и от указанного формата времени в файле журнала. Также обратите внимание, что это не содержит много проверок безопасности, но вы должны получить представление о том, как изменить его в соответствии с вашими потребностями.
sys-log
в начале и Log
в конце, чтобы повысить избирательность на тот случай, если может быть другое содержимое. Как уже говорилось, это очень элементарный тест, но, опять же, вы можете понять, как сделать его более конкретным. date
через sprintf
и делегирования задачи оболочке. Login
, отметка времени сохраняется в массиве login
с именем пользователя как "индекс массива". Logout
, продолжительность будет рассчитана и добавлена к массиву logtime
, содержащему общее время журнала -для всех пользователей на данный момент. logtime
и преобразования лог-времени из секунд в минуты путем простого деления. Вы можете позвонить через
awk -f calc_logtime.awk logfile.dat
Похоже, это работа для dateutils
. Выловите соответствующие части с помощью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
Выход:
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
И передать его в цикл while -:
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
Выход:
user1 994
user2 1082
user3 0
Примечание. :вы можете изменить переключатель -f
команды ddiff
для выбора другого формата времени. Здесь мы используем истекшие -секунды.
С помощью GNU awk для функций времени и gensub ()и массивов массивов:
$ 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
Это будет работать на несколько порядков быстрее, чем вызов Unix date
из awk, поскольку последний должен каждый раз создавать подоболочку для этого.
Если вы хотите также получить отчет о пользователях, которые вошли в систему, но не вышли из нее, когда вы запускаете скрипт, например. user4
в этом модифицированном входном файле:
$ 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
Тогда просто подправьте скрипт:
$ 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
Если вам нужно найти случаи, когда пользователь вошел в систему снова, когда он уже вошел в систему без выхода из системы между ними, или обрабатывать выходы из системы без связанного входа в систему по-другому или сделать что-то еще, то это тоже были бы тривиальные настройки.
Следующий perl
скрипт использует модуль Date ::Parse из коллекции TimeDate для анализа даты и времени из каждой записи вместо того, чтобы полагаться на дату GNU для выполнения Это. Это, вероятно, упаковано для вашего дистрибутива (на debian, apt install libtimedate-perl
), в противном случае установите его с помощью cpan
.
Сценарий работает, используя последнее поле каждой строки ввода (, которое выглядит как идентификатор сеанса ), в качестве ключа верхнего -уровня для хэша -из -хэшей (. Структура данных HoH )называется %sessions
. Каждый элемент %sessions представляет собой анонимный хеш, содержащий ключи user
, login
и logout
.
После того, как весь файл прочитан и проанализирован, кумулятивные итоги для каждого пользователя вычисляются (и сохраняются в другом ассоциативном массиве, %users
), а затем распечатываются. Вывод сортируется по имени пользователя.
#!/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);
};
Сохраните его как, например, login-times.pl
и сделайте его исполняемым с помощью chmod +x login-times.pl
. Запустите его как:
$./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
К вашему сведению, данные в %sessions
HoH выглядят следующим образом:
%sessions = {
13272 => { login => 1620424730, logout => 1620424730, user => "user3" },
13473 => { login => 1620292323, logout => 1620293317, user => "user1" },
14195 => { login => 1620292526, logout => 1620293608, user => "user2" },
}
Для сеанса вполне возможно не иметь отметки времени ни для входа, ни для выхода. Было бы легко напечатать сообщение в STDERR, если бы они отсутствовали. Или справляться с такими аномалиями, как вам угодно. Сценарий выше просто игнорирует их.
Для полноты картины данные в %users
выглядят следующим образом:
%users = { user1 => 994, user2 => 1082, user3 => 0 }
Кстати, эти структуры данных были напечатаны с помощью модуля Data ::Dump , который очень полезен для отладки и т. д. Имя пакета Debian — libdata-dump-perl
, в других дистрибутивах он, вероятно, есть. В противном случае установите его с помощью cpan
.
Чтобы напечатать их, я добавил следующее в конец скрипта:
use Data::Dump qw(dump);
print "%sessions = ", dump(\%sessions);
print "%users = ", dump(\%users)
Наконец, IP-адрес фиксируется с помощью функции split
в сценарии, но не используется. Его можно легко добавить в хэш сеанса и использовать для печати сводки в одну строку -для каждой пары входа и выхода.Для форматирования дат можно использовать модуль Date ::Format из той же коллекции Time::Date
.
Например:
добавить use Date::Format;
после строки use Date::Parse;
Раскомментируйте $session{$s}->{IP} = $ip;
в цикле while(<>)
.
Используйте что-то вроде следующего, чтобы распечатать данные:
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;
};
Вывод будет таким, как:
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