时间差Bash脚本

3
我正在尝试编写一个bash脚本,计算用户首次登录和最近一次登录之间的时间差。感谢您的帮助:)
以下是我目前的代码:
read -p "Enter a user ID: " ID
echo "You entered the following ID(s): $ID"

#/bin/egrep -i "^$ID" /etc/passwd

echo -n "The users real name is: "
/bin/grep "^$ID" /etc/passwd | cut -f5 -d :

echo -n "$ID's first login time is: "
l1=`last "$ID" | tail -n 1`


echo -n "$ID's last login time is: "
l2=`last "$ID" | head -n 1`

echo $l1
echo $l2

echo -n "The time difference is $(l1-l2) "
2个回答

1

你的脚本似乎包含了一些错误。

/bin/grep "^$ID" /etc/passwd | cut -f5 -d :

这个命令会在 /etc/passwd 文件中查找 uid(用户id)的开头部分(使用了开始标志^),但事实上,passwd文件总是以用户名开始,而不是uid。
也许你对用户id的概念有所困惑;在UNIX系统中,用户id指的是每个用户都有的数字ID,而用户名指的是登录时输入的登录名。

无论如何,使用getent命令是更可靠的方法;我们可以用grep搜索:$uid:,但如果一个组id与一个用户id相同,那么这种方法可能会出错(还有其他场景)。getent命令不仅能用于用户id,还能用于用户名。

此外,使用/bin/grep几乎总是不好的选择;在$PATH中查找通常更为优秀(所以只需要用grep即可)。

l1=`last "$ID" | tail -n 1`
last 期望一个用户名,而不是用户ID;也许有一种类型的last 命令可以接受UID(?);无论如何,使用用户名更加可靠。

echo -n "时间差为 $(l1-l2)"

last 命令返回的日期是字符串格式的(Sat Nov 1 00:39);你不能像整数一样直接相减,需要先使用date解析它们。
这是一个可行版本的示例;我还进行了其他(次要)改进,可能对您有用:
#!/bin/sh

# Keep asking for the uid until *something* is entered
while :; do
    read -p "Enter a user ID: " uid
    [ -n "$uid" ] && break
done

# Get line from /etc/passwd
passwd=$(getent passwd "$uid")

# Exit code was non-zero; the uid is unknown
if [ $? -ne 0 ]; then
    echo "User id '$uid' is unknown"
    exit 1
fi

# Get data from passwd
username=$(echo "$passwd" | cut -d: -f1)
realname=$(echo "$passwd" | cut -d: -f5)


# Get info from last, strip last 2 lines since they're not useful for us. Use
# ISO format so that date can parse them
lastlog=$(last --time-format iso "$username" | head -n-2)

# Get first & last line; we only need the date
last_login=$(echo "$lastlog" | head -n1 | tr -s ' ' | cut -d ' ' -f 4)
first_login=$(echo "$lastlog" | tail -n1 | tr -s ' ' | cut -d ' ' -f 4)

# Parse dates with date, output time in seconds since 1-1-1970 ('epoch')
diff=$(( $(date --date "$last_login" +%s) - $(date --date "$first_login" +%s) ))

# Format the date
diff_fmt=$(date --date @$diff +'%d days %H hours %M minutes %S seconds')

# Output info
echo "Found user $username ($realname) for userid $uid"
echo "First recorded login: $first_login"
echo "Last recorded login: $last_login"
echo "Difference: $diff_fmt (total of $diff seconds)"

很遗憾,这只能在Linux系统上运行;要使其在所有的UNIX版本上运行需要更多的工作(shell脚本通常难以实现可移植性)

示例输出:

[~]% sh test.sh
Enter a user ID: 1001
Found user martin (Martin Tournoij) for userid 1001
First recorded login: 2014-11-01T00:13:28+0100
Last recorded login: 2014-11-30T06:08:54+0100
Difference: 30 days 06 hours 55 minutes 26 seconds (total of 2526926 seconds)

比我的更全面。虽然你之前发布帖子,但我不确定为什么直到现在才看到它。 - Dylan Madisetti
1
@DylanMadisetti 谢谢 :-) 我大约40分钟前发布了它,但是觉得它可能会更好/更全面,所以我删除了我的帖子。我进行了改进,并在你发布答案后立即将其恢复 :-) - Martin Tournoij
很抱歉要告诉你这个消息,但是你的代码只能在Linux上运行 - 它不具备远程可移植性。getent命令是特定于Linux的(例如,在Mac OS X上不可用)。last --time-iso选项仅适用于GNU的last(例如,在Mac OS X上不可用)。你使用的所有主要date选项都不直接可用,除非使用GNU的date命令。虽然GNU代码可以安装在任何类Unix系统上,但该代码在大多数非Linux系统上不能直接运行。 - Jonathan Leffler
@JonathanLeffler 谢谢;在特定平台上做这种事情很难干净利落;例如,FreeBSD有自己的扩展来执行此操作,这些扩展与GNU date不兼容,并且getent并非仅限于Linux,我知道至少FreeBSD、NetBSD和Solaris都有它,但显然没有OSX... 无论如何,我假设这个问题是关于Linux的...(我调整了兼容性说明)。 - Martin Tournoij

1
这是基于您想提供用户名而不是ID的假设。
首先,您需要正确执行捕获。
l1=$(last "$ID" | tail -n 1)
l2=$(last "$ID" | head -n 1)

在我的实例中左侧。
l1="wtmp begins Sun Nov  9 07:32:12 2014"
l2="graham   pts/11       :0               Sat Nov 29 22:13   still logged in"  

由于我们只需要日期,因此这不是一个好选择。

那么让我们来解决这个问题。下面是一些hacky的解析方法,以获取仅时间:

l1=$(last -RF | grep $ID | tail -n 1 | tr -s ' ' | cut -d ' ' -f 3,4,5,6)
l2=$(last -RF "$ID" | head -n 1 | tr -s ' ' | cut -d ' ' -f 3,4,5,6)

我对l1进行grep搜索,因为last会留下最后登录的记录,但出于一致性考虑,我只获取最后一行。 last -RF删除主机(-R),因为我们不感兴趣并使时间更加美观(-F)。tr修剪所有额外的空格并且使用空格作为分隔符,获取日期。
我们想要计算之间的时间差,所以让我们将两个日期转换为datetime字符串并相减:
a=$(date -ud "$l2" +"%s") 
b=$(date -ud "$l1" +"%s")

d=$(( a-b ))

最后让我们打印。
echo "$ID's last login time is: $l1"
echo "$ID's first login time is: $l2"
echo "The time difference is $d seconds"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接