Рассчитать продолжительность входа пользователя в систему в минутах из файла

ви

внутри типа vi

:set nowrap

Я полагаю, что есть плагин для vi, который даст вам поведение, похожее на хвост.

4
15.06.2021, 13:37
4 ответа

Хотя этот сайт "не является службой написания скриптов -" ; ), это небольшое упражнение, поэтому я предлагаю следующую 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в конце, чтобы повысить избирательность на тот случай, если может быть другое содержимое. Как уже говорилось, это очень элементарный тест, но, опять же, вы можете понять, как сделать его более конкретным.
  • Пользователь будет извлечен как 5-е поле, разделенное -пробелом в строке.
  • Действие будет извлечено как поле, разделенное 7-м пробелом -в строке.
  • Отметка времени действия будет преобразована в «секунды с начала эпохи» путем создания вызова dateчерез sprintfи делегирования задачи оболочке.
  • Если действие Login, отметка времени сохраняется в массиве loginс именем пользователя как "индекс массива".
  • Если действие Logout, продолжительность будет рассчитана и добавлена ​​к массиву logtime, содержащему общее время журнала -для всех пользователей на данный момент.
  • В конце -файла -будет сгенерирован отчет путем перебора всех «индексов массива» logtimeи преобразования лог-времени из секунд в минуты путем простого деления.

Вы можете позвонить через

awk -f calc_logtime.awk logfile.dat
5
28.07.2021, 11:25

Похоже, это работа для 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для выбора другого формата времени. Здесь мы используем истекшие -секунды.

1
28.07.2021, 11:25

С помощью 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

Если вам нужно найти случаи, когда пользователь вошел в систему снова, когда он уже вошел в систему без выхода из системы между ними, или обрабатывать выходы из системы без связанного входа в систему по-другому или сделать что-то еще, то это тоже были бы тривиальные настройки.

5
28.07.2021, 11:25

Следующий 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

К вашему сведению, данные в %sessionsHoH выглядят следующим образом:

%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.

Например:

  1. добавить use Date::Format;после строки use Date::Parse;

  2. Раскомментируйте $session{$s}->{IP} = $ip;в цикле while(<>).

  3. Используйте что-то вроде следующего, чтобы распечатать данные:

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
5
28.07.2021, 11:25

Теги

Похожие вопросы