我强烈建议不要解析 的输出last
,因为它的输出可能因实现而异,并且解析登录/注销日期容易出错。此外,似乎几乎所有的实现都不支持-F
或类似的,没有你完全不走运,因为你需要年份信息。理论上,您可以检查两个连续行上是否存在从一个月到另一个最近的飞跃(例如 Jan->Dec 表示年份变化),但这种启发式方法是有缺陷的 - 您无法猜测正确的年份(s) 正确。例如,以一年没有人登录的罕见情况为例。
如果您绝对必须/想要以任何一种方式解析其输出,请不要仅使用 bash/awk/cut/... 来执行此操作,原因如上所述。要获得会话持续时间,您要么必须自己解析漂亮打印的登录/注销日期,要么解析已经计算的持续时间,这也是漂亮打印的并且可能因实施而异(例如,它不仅仅是小时和分钟。如何获得天数/weeks/years 在该列中表示?)。仅使用 bash/awk 执行此操作将是一场噩梦,甚至比我下面的脚本更容易损坏 - 请不要这样做。
最好和最简单的解决方案是编写一个wtmp
直接对数据进行操作的小型 C 程序或脚本man wtmp
( ; 登录是一条记录,注销是第二条记录)。有关它如何读取内容的参考,请参阅busybox 的last
实现。如果您想以正确的方式进行操作,这就是要走的路。
话虽如此,我想出了下面的快速'n'dirty(perl)解决方案。它不运行last
命令,你必须自己输入正确的输入,否则它会爆炸。如果您的last
输出看起来与我的不同,不支持-F
或Date::Parse
无法解析您的last
命令打印的格式,它也会爆炸。有很大的改进空间,但这应该让你开始。
笔记
-F
需要last
打印完整日期(我们需要它来获取年份,否则我们无法从其输出中确定正确的时间戳)
-i
告诉 last 输出 IP 地址,这只会使其输出更易于解析
- 它不解析会话持续时间列,而是解析登录/注销日期,将它们转换为纪元时间并计算差异以获得会话持续时间。除了 using 之外,解析日期没有其他魔法
Date::Parse
,这意味着它必须排除所有没有正确登录/注销日期的会话(即,它们仍处于登录状态或会话因重启、崩溃等),因此这些会话不会成为计算输出的一部分!
- 默认为 7 天,但您可以在命令行中使用
-d
开关更改此设置
代码
#!/usr/bin/perl
use strict;
use warnings;
use Date::Parse;
use Getopt::Std;
our $opt_d;
getopt('d');
my $days = $opt_d || 7;
my $since = time() - (60 * 60 * 24 * $days);
my %data;
while (<>)
{
chomp;
next if /ssh|reboot|down|crash|still logged in/;
# last -Fi gives this on my box
# username line ip Mon Apr 1 18:17:49 2013 - Tue Apr 2 01:00:45 2013 (06:42)
my ($user, undef, undef, $date_from, $date_to) = /^(\S+)\s+(\S+)\s+([0-9.]+)\s+([[:alnum:]:\s]+)\s+-\s+([[:alnum:]:\s]+[^\s])\s+\(.+\)/;
my $time_from = str2time($date_from);
last if $time_from < $since;
my $time_to = str2time($date_to);
$data{$user}{"count"}++;
$data{$user}{"duration"} += $time_to - $time_from;
# print "$user|$line|$ip|$date_from|$date_to\n";
}
print "login history for the last $days day(s):\n\n";
if (keys %data > 0)
{
foreach my $user (keys %data)
{
my $duration = $data{$user}{"duration"};
printf "%s was logged in %d time(s) for a total of %d day(s), %d hour(s) and %d minute(s)\n",
$user,
$data{$user}{"count"},
($duration / (24 * 60 * 60)),
($duration / (60 * 60 )) % 24,
($duration / 60 ) % 60,
}
}
else
{
print "no logins during the specified time period\n";
}
示例用法
$ last -Fi | ./last_parse.pl -d 700
login history for the last 700 day(s):
root was logged in 25 time(s) for a total of 36 day(s), 12 hour(s) and 35 minute(s)
foobar was logged in 362 time(s) for a total of 146 day(s), 17 hour(s) and 17 minute(s)
quux was logged in 3 time(s) for a total of 0 day(s), 0 hour(s) and 4 minute(s)
$