我一直在给出一个输入文件,它看起来像这样:
ID PID PPID C STIME TTY TIME CMD
adz110 5344 5334 0 08:47 pts/2 00:00:00 bash
dmq292 6908 6854 0 08:53 pts/1 00:00:00 bash
adz110 7227 7150 0 08:54 pts/9 00:00:00 who
erg474 7466 7461 0 08:54 pts/10 00:00:00 ls
dmq292 7966 7960 0 08:55 pts/13 00:00:00 assign1.sh if of
xle135 8636 8628 0 08:58 pts/15 00:00:00 bash
xle135 8983 8636 0 08:59 pts/15 00:00:00 ssh ctf.cs.utsarr.net
zeh458 9057 1980 0 08:59 pts/7 00:00:00 vim prog.c
HanSolo 9150 9139 0 08:59 pts/16 00:00:00 ps -af
它需要像这样结束:
User: adz110
bash
who
User: dmq292
bash
assign1.sh if of
User: erg474
ls
User: xle135
bash
ssh ctf.cs.utsarr.net
User: zeh458
vim prog.c
Earliest Start Time:
adz110 5344 5334 0 08:47 pts/2 00:00:00 bash
Latest Start Time
xle135 8983 8636 0 08:59 pts /15 00:00:00 ssh ctf.cs.utsarr.net
到目前为止,我所写的是在我写下的file.awk中:
/[a-z]{3}[0-9]{3}/
{
print $1
if( match($1 , arg1) )
{
for(i=8; i <= NF ; i ++)
printf("%s", $i);
printf("\n");
}
}
END {
print " Earliest Start Time:"
print "Latest Start Time: "
}
因此,不是为具有与[a-z] [0-9] {3}匹配的用户名的每个用户打印,而是打印cmd以及没有任何空格的用户名。我在理解关联数组和使用特殊变量(如NR,RS等)时遇到了很多麻烦。
试试这个,file.awk
:
$1 !~ /[a-z]{3}[0-9]{3}/ {next;}
!fstTime {fstTime=$5; lstTime=$5; first=$0; last = $0;}
!($1 in a) {a[$1];users[++ind]=$1;}
{ cmd=$8; for(i=9;i<=NF;i++) cmd=cmd OFS $i;
cmds[$1] = cmds[$1] ? cmds[$1] "\n " cmd : " " cmd;
if ($5 < fstTime) { fstTime=$5; first=$0; }
if ($5 > lstTime) { lstTime=$5; last = $0; }
}
END {
for(i=1;i<=length(a);i++) {
print "User: " users[i];
print cmds[users[i]];
}
print "Earliest Start Time:\n" first "\n\nLatest Start Time:\n" last;
}
awk -f file.awk fileToTest.txt
收益率:
User: adz110
bash
who
User: dmq292
bash
assign1.sh if of
User: erg474
ls
User: xle135
bash
ssh ctf.cs.utsarr.net
User: zeh458
vim prog.c
Earliest Start Time:
adz110 5344 5334 0 08:47 pts/2 00:00:00 bash
Latest Start Time:
xle135 8983 8636 0 08:59 pts/15 00:00:00 ssh ctf.cs.utsarr.net
如果你想让zeh458 9057 1980 0 08:59 pts/7 00:00:00 vim prog.c
成为Latest Start Time:
线,只需将($5 > lstTime)
更改为($5 >= lstTime)
即可。
您希望仅对第一个字段应用正则表达式,并以适合打印输出的格式为内存中的每个用户收集值。
观察列表是固定宽度的;所以程序名称和参数位于从第55列开始的字段中。同样,时间戳在第28-32列中。
awk 'NR > 1 && $1 ~ /^[a-z]{3}[0-9]{3}$/ {
when = substr($0, 28, 5)
command = substr($0, 55)
if ($1 in user) {
# Append this command to previous value
user[$1] = user[$1] ORS " " command
} else {
# Create a new item in this associative array
user[$1] = " " command
}
if (NR==2 || when > max) { max=when; maxcmd = $0 }
if (NR==2 || when < min) { min=when; mincmd = $0 }
}
END {
# XXX TODO: sort array?
for (u in user) printf "User %s:\n%s\n", u, user[u]
print "Earliest start time"; print mincmd
print "Lastest start time"; print maxcmd
}' filename
因此,当我们第一次看到特定用户时,它们将不会出现在users
中,因此我们只将第一个命令缩进四个空格作为关联数组中该键的值。如果我们再次看到它们,我们会在该值附加另一行,使用换行符(ORS
)和前面的四个空格。
NR
是当前行号 - 我们跳过NR==1
以避免捕获标题行,并且使用NR==2
我们知道这是我们正在处理的第一行,因此我们将max
和min
设置为其基线值。
理想情况下,你还应该将时间戳规范化为规范形式,这样你就可以在“21:23”之前对“昨天21:24”进行排序,但我不会用这个简单的答案去那里。您可能也希望在其他各个地方添加装饰。 (或者你可以依赖输入似乎按进程开始时间排序的事实?)
如果要对用户名进行排序,GNU Awk内置了数组排序;对于其他Awks,您需要编写自己的简单排序函数,或使用外部管道。
输入文件中存在一个混淆(您的命令输出显示一些具有相同时间的进程,例如xle135
和zeh458
)。因此,我提出的解决方案将对您的输入文件进行排序(如果您需要代替它,您可以输入命令),然后它将计算出所有条目中的最大和最小时间段。如果某些东西具有相同的时间戳,它将不会连接它们的值(尽管我们也可以这样做);相反,它将打印最新排序的一个(仅限最新的时间戳)。
awk '
/^ID/{
next
}
{
split($5,array,":")
seconds=array[1]*60+array[2]
}
FNR==NR{
a[$1]++
tim[seconds]=$0
next
}
a[$1]==2{
print "User: " $1 ORS " " $NF
getline
sub(/.*:00/,"")
print" " $0
}
a[$1]==1{
val=$0
sub(/.*:00/,"",val)
print "User: " $1 ORS " " val
}
{
min=min<seconds?(min?min:seconds):seconds
max=max>seconds?max:seconds
}
END{
print "Earliest Start Time:" ORS tim[min] ORS "Latest Start Time" ORS tim[max]
}
' <(sort -k1,5 Input_file) <(sort -k1,5 Input_file)
输出如下。
User: adz110
bash
who
User: dmq292
bash
assign1.sh if of
User: erg474
ls
User: HanSolo
ps -af
User: xle135
bash
ssh ctf.cs.utsarr.net
User: zeh458
vim prog.c
Earliest Start Time:
adz110 5344 5334 0 08:47 pts/2 00:00:00 bash
Latest Start Time
zeh458 9057 1980 0 08:59 pts/7 00:00:00 vim prog.c
假设如果给定用户多次运行相同的命令,您希望该命令在该用户的输出中多次出现:
$ cat tst.awk
NR == 1 { next }
/^[a-z]{3}[0-9]{3}/ {
user = $1
users[user]
cmd = $0
sub(/([^[:space:]]+[[:space:]]+){7}/,"",cmd)
cmds[user,++numCmds[user]] = cmd
stime = $5
if ( (earliestStime == "") || (stime < earliestStime) ) {
earliestStime = stime
earliestData = $0
}
if ( (latestStime == "") || (stime > latestStime) ) {
latestStime = stime
latestData = $0
}
}
END {
for (user in users) {
printf "User: %s\n", user
for (cmdNr=1; cmdNr<=numCmds[user]; cmdNr++) {
printf " %s\n", cmds[user,cmdNr]
}
}
print "\nEarliest Start Time:"
print earliestData
print "\nLatest Start Time:"
print latestData
}
.
$ awk -f tst.awk file
User: xle135
bash
ssh ctf.cs.utsarr.net
User: zeh458
vim prog.c
User: dmq292
bash
assign1.sh if of
User: erg474
ls
User: adz110
bash
who
Earliest Start Time:
adz110 5344 5334 0 08:47 pts/2 00:00:00 bash
Latest Start Time:
xle135 8983 8636 0 08:59 pts/15 00:00:00 ssh ctf.cs.utsarr.net
如果输出中的用户或命令的顺序很重要,那么告诉我们订单应该是什么,并且它很容易适应,例如,保留输入顺序或在END部分的每个循环之前使用GNU awk设置PROCINFO["sorted_in"]
。