파일에서 사용자의 로그온 시간을 분 단위로 계산합니다.

파일에서 사용자의 로그온 시간을 분 단위로 계산합니다.

Linux 시스템에 다음과 같은 파일이 있습니다.

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

그리고 각 사용자가 로그인하고 로그아웃하는 데 소요된 시간을 분 단위로 계산하려고 합니다. 사용자당 한 번의 로그인/로그아웃만 가능하며 모든 사용자에 대한 보고서를 한 번에 생성하고 싶습니다.

내가 시도한 것:

먼저 사용자를 먼저 추출하려고 했습니다.

users=$(awk -v RS=" " '/login/{getline;print $0}' data)

사용자(로그인)를 반환한 다음 사용자가 로그인한 시간을 추출하려고 시도했지만 현재 멈췄습니다. 어떤 도움이라도 주시면 감사하겠습니다!

편집: 다음을 수행하여 사용자와 날짜를 얻을 수 있습니다.

users=$(grep -o 'user[0-9]' data)
dates=$(grep -o '[0-2][0-9]:[0-5][0-9]:[0-5][0-9]' data)

완전한 해결책을 찾으면 여기에서 공유하겠습니다.

답변1

이 사이트는 "스크립트 작성 서비스는 아니지만";) 이것은 좋은 작은 연습이므로 다음 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

답변2

시간 함수와 gensub() 및 배열 배열에 대한 GNU awk를 사용합니다.

$ 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

dateawk에서 Unix를 호출하려면 매번 서브셸을 생성해야 하기 때문에 이는 awk에서 Unix를 호출하는 것보다 훨씬 빠르게 실행됩니다 .

스크립트를 실행할 때 로그인했지만 로그아웃하지 않은 사용자에 대한 보고서도 얻으려면 다음과 같이 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

사용자가 이미 로그인한 상태에서 중간에 로그아웃 없이 다시 로그인한 경우를 찾거나 관련 로그인 없이 로그아웃을 다르게 처리하거나 다른 작업을 수행해야 하는 경우에도 사소한 조정에 불과합니다.

답변3

다음 perl스크립트는날짜::분석모듈의시간날짜GNU 날짜에 의존하는 대신 각 레코드의 날짜 및 시간을 구문 분석하는 컬렉션입니다. 이것은 아마도 배포판(debian, apt install libtimedate-perl)용으로 패키지되어 있을 것입니다. 그렇지 않으면 cpan.

스크립트는 각 입력 줄의 마지막 필드(세션 ID로 나타남)를 HoH(Hash-of-Hashes) 데이터 구조에 대한 최상위 키로 사용하여 작동합니다 %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

참고로 HoH의 데이터는 %sessions다음과 같습니다.

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

그런데, 이 데이터 구조는 다음과 같이 인쇄되었습니다.데이터::덤프디버깅 등에 매우 유용한 모듈입니다. 데비안 패키지 이름은 이고 libdata-dump-perl, 다른 배포판에도 있을 것입니다. 그렇지 않으면 으로 설치하십시오 cpan.

이를 인쇄하기 위해 스크립트 끝에 다음을 추가했습니다.

use Data::Dump qw(dump);
print "%sessions = ", dump(\%sessions);
print "%users = ", dump(\%users)

마지막으로 IP 주소는 split스크립트의 함수로 캡처되지만 사용되지는 않습니다. 이는 세션 해시에 쉽게 추가될 수 있으며 각 로그인 및 로그아웃 쌍에 대한 한 줄 요약을 인쇄하는 데 사용됩니다. 그만큼날짜 형식동일한 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

답변4

이것은 에 대한 직업처럼 들립니다 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

참고: ddiff명령 -f스위치를 변경하여 다른 시간 형식을 선택할 수 있습니다. 여기서는 초 경과 시간을 사용하고 있습니다.

관련 정보