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

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

내 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)
    }
}

date이는 GNU 명령(GNU/Linux 시스템의 표준 도구 모음의 일부) 사용과 로그 파일에 지정된 시간 형식 에 따라 달라집니다 . 또한 여기에는 많은 보안 검사가 포함되어 있지 않지만 필요에 맞게 수정하는 방법을 알아야 합니다.

  • 다음을 포함하는 줄을 찾습니다.둘 다sys-logLog다른 것이 있을 경우 선택성을 높이기 위해 시작과 끝 부분에 가까운 문자열입니다. 앞서 언급한 것처럼 이는 매우 기본적인 테스트이지만 다시 한 번 더 구체적으로 만드는 방법을 배울 수 있습니다.
  • 사용자는 행에서 공백으로 구분된 다섯 번째 필드로 추출됩니다.
  • 작업은 행의 공백으로 구분된 7번째 필드로 추출됩니다.
  • date호출을 생성 sprintf하고 작업을 셸에 위임함으로써 작업의 타임스탬프가 "에포크 이후 초"로 변환됩니다.
  • 작업이 이면 타임스탬프는 사용자 이름을 "배열 인덱스"로 사용하여 Login배열에 저장됩니다 .login
  • 작업이 인 경우 Logout기간이 계산되어 logtime현재까지 모든 사용자의 총 로그인 시간이 포함된 배열에 추가됩니다.
  • 파일 끝에서 모든 "배열 인덱스"를 반복하고 logtime간단한 나눗셈을 통해 로그 시간을 초에서 분으로 변환하여 보고서가 생성됩니다.

전화로 문의하시면 됩니다

awk -f calc_logtime.awk logfile.dat

답변2

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

이는 date매번 하위 쉘을 생성해야 하는 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 Date에 의존하는 대신 각 레코드의 날짜와 시간을 구문 분석하는 컬렉션입니다. 이는 배포용으로 패키지될 수 있습니다( debian apt install libtimedate-perl). 그렇지 않으면 를 사용하세요 cpan.

스크립트는 각 입력 줄의 마지막 필드(세션 ID로 나타남)를 Hash-Hash(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

참고용으로 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)

split마지막으로 스크립트의 함수를 사용하여 IP 주소를 캡처하되 사용하지는 마세요. 이는 세션 해시에 쉽게 추가될 수 있으며 각 로그인 및 로그아웃 쌍에 대한 한 줄 요약을 인쇄하는 데 사용됩니다. 이것날짜 형식동일한 컬렉션 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스위치를 변경하여 다른 시간 형식을 선택할 수 있습니다 . 여기서는 경과된 초를 사용하고 있습니다.

관련 정보