我有一个名为 session.log 的文件,它包含一些文本数据。第一列包含序列号。第二列包含用户名。第三列包含上次登录日期。第四列包含结果。
一名用户多次登录。我需要找到每个唯一用户的上次登录日期。所以我写了一个shell脚本。输出将显示每个唯一用户(第二列)的最新登录日期(第三列)的行。输出不应多次包含相同的用户名。
$ cat 会话.log 1 u1 2018-05-19 通过 2 u2 2018-06-15 通过 3 u3 2018-05-18 通过 4 u4 2018-05-17 通过 5 u2 2018-05-14 通过 6 u4 2018-07-11 通过 7 U1 2018-05-16 通过 8 u3 2018-05-13 通过 9 U1 2018-08-12 通过 10 u1 2018-05-10 通过
我尝试过的:
( awk {print} session.log | cut -f2 -d' ' | sort | uniq ) > store.txt
for line in $(cat "store.txt")
do
touch "$line.txt"
grep "$line" session.log > "$line.txt"
temp=$(sort -k3 "$line.txt" | awk 'END{print}')
echo $temp > "$line.txt"
cat "$line.txt"
done
输出
$ ./sort.sh 9 U1 2018-08-12 通过 2 u2 2018-06-15 通过 3 u3 2018-05-18 通过 6 u4 2018-07-11 通过
shell 脚本适用于日期格式 (yyyy-mm-dd) 和 (yyyy/mm/dd)。有没有更好的代码来完成相同的工作?使用awk
我们如何做到这一点?
编辑:
$ cat sort.sh
( awk {print} session.log | cut -f2 -d' ' | sort | uniq ) > store.txt
for line in $(cat "store.txt")
do
#touch "$line.txt"
grep "$line" session.log > "$line.txt"
echo $(sort -k3 "$line.txt" | awk 'END{print}')
#temp=$(sort -k3 "$line.txt" | awk 'END{print}')
#echo $temp > "$line.txt"
#cat "$line.txt"
done
rm -f store.txt
答案1
$ sort -k 3,3r session.log | awk '!seen[$2]++ { print }'
9 u1 2018-08-12 pass
6 u4 2018-07-11 pass
2 u2 2018-06-15 pass
3 u3 2018-05-18 pass
({ print }
可以完全删除,我只是将其包括在内以显示会发生什么。默认操作是如果条件为真则打印整个输入记录。)
这将按日期递减(首先是最近的日期)对您拥有的文件进行排序。该awk
程序读取排序后的数据并打印出为每个用户找到的第一个条目。该seen
变量是一个使用用户名作为键的关联数组/哈希。如果某个用户的值为零,则表示尚未看到该用户,因此会打印文件中的行。
你的代码和我的注释:
# get list of unique usernames from log:
( awk {print} session.log | cut -f2 -d' ' | sort | uniq ) > store.txt
# shorter equivalent:
# cut -d ' ' -f2 <session.log | sort -u >store.txt
# loop over the usernames:
for line in $(cat "store.txt")
do
# get entries related to this user:
grep "$line" session.log > "$line.txt"
# echo the last entry:
echo $(sort -k3 "$line.txt" | awk 'END{print}')
# shorter equivalent of both of the above commands:
# awk -v user="$line" '$2 == user { print }' session.log | sort -k3,3 | tail -n 1
done
rm -f store.txt
因此,基于 shell 循环的替代方法:
cut -d ' ' -f2 <session.log | sort -u |
while read username; do
awk -v user="$username" '$2 == user { print }' session.log | sort -k 3,3 | tail -n 1
done
同样,上面的脚本{ print }
中可能会省略awk
。
这仍然会为每个用户对会话日志的子集进行一次排序,这有点浪费。
按日期递减对日志进行预排序:
sort -k3,3r -o sorted.log session.log
cut -d ' ' -f2 <session.log | sort -u |
while read username; do
awk -v user="$username" '$2 == user { print; exit }' sorted.log
done
rm sorted.log
这仍然需要我们从每个用户的顶部扫描一次日志。自然的改进是允许awk
跟踪用户已经看到的内容和未看到的用户,这给了你我在顶部的答案。