问题描述
我有一个这样的文件,其中包含多行的多个字段。我想显示其中一些,同时用另一个命令处理其中一个。
TITLE,OpenVPN ...
HEADER,CLIENT_LIST,Common Name,Real Address,Virtual Address,Virtual IPv6 Address,Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t),Username,Client ID,Peer ID
CLIENT_LIST,name1,1.1.1.1:1,10.0.0.1,2692253,3765861,Wed Jun 23 12:51:08 2021,1624452668,4727,0
CLIENT_LIST,name2,2.2.2.2:2,10.0.0.2,1571221,2080242,Thu Jul 1 19:24:10 2021,1625167450,5625,name3,3.3.3.3:3,10.0.0.3,2670410,3736957,Wed Jun 23 16:20:51 2021,1624465251,4747,0
...
预期的输出是这样的:
name1 10.0.0.1 2021-06-23 12:51:08
name2 10.0.0.2 2021-07-01 19:24:10
name3 10.0.0.3 2021-06-23 16:20:51
我现在的命令是这样的:
grep '^CLIENT_LIST,' /var/run/ovpn-server.status |awk -F',' '{print $2 $4 $9}' |sort
它打印所需的字段,但不会将时间戳转换为格式化的时间。这是为此的命令:
date -d @1624452668 +"%Y-%m-%d %H:%M:%s"
如何将 date
命令集成到 awk
脚本中?或者还有什么其他解决方案可以实现这一点?
我还打算使用 column
命令将输出放入列/表格布局中,我之前已经这样做过,所以这不是问题的一部分。
解决方法
您可以使用此awk
:
awk -F,-v OFS='\t' '$1 == "CLIENT_LIST" {
cmd = "date +\047%Y-%m-%d %H:%M:%S\047 -d\047@" $9 "\047"
print $2,$4,((cmd | getline dt) > 0 ? dt : $9)
close(dt)
}' file
name1 10.0.0.1 2021-06-23 08:51:08
name2 10.0.0.2 2021-07-01 15:24:10
name3 10.0.0.3 2021-06-23 12:20:51
说明:
-
-F,-v OFS='\t'
:将输入字段分隔符设置为,
,将输出字段分隔符设置为制表符 -
'$1 == "CLIENT_LIST"
:当第一个字段是CLIENT_LIST
时执行 -
cmd = "date +\047%Y-%m-%d %H:%M:%S\047 -d\047@" $9 "\047"
:使用date
格式化 -
cmd | getline dt
调用外部date
命令 -
(cmd | getline dt) > 0
:当date
命令成功时 -
print
:打印date
字段的第 2、4 和输出
$9
命令
如果您实际上只想重新格式化 $8
中的日期 + 时间,而不是将自纪元以来的秒数从 $9
转换为日期 + 时间,那么您只需执行以下操作即可比调用 date
的速度要快得多,因为这需要 awk
在每个输入行中生成一个子外壳,以便从该子外壳调用 date
,这将非常慢。
在每个 Unix 机器上的任何 shell 中使用任何 awk:
$ cat tst.awk
BEGIN { FS=","; OFS="\t" }
NR > 2 {
split($8,t," ")
mthNr = (index("JanFebMarAprMayJunJulAugSepOctNovDec",t[2])+2)/3
print $2,sprintf("%04d-%02d-%02d %s",t[5],mthNr,t[3],t[4])
}
$ awk -f tst.awk file
name1 10.0.0.1 2021-06-23 12:51:08
name2 10.0.0.2 2021-07-01 19:24:10
name3 10.0.0.3 2021-06-23 16:20:51
或者如果你真的想使用 $9 的纪元秒,那么使用 GNU awk for strftime()
这样你就不必产生子 shell 来调用 date
(但请注意,输出现在变成了 TZ -dependent,就像 date
):
$ cat tst.awk
BEGIN { FS=","; OFS="\t" }
NR > 2 {
print $2,strftime("%F %T",$9)
}
$ awk -f tst.awk file
name1 10.0.0.1 2021-06-23 07:51:08
name2 10.0.0.2 2021-07-01 14:24:10
name3 10.0.0.3 2021-06-23 11:20:51
$ TZ=UTC awk -f tst.awk file
name1 10.0.0.1 2021-06-23 12:51:08
name2 10.0.0.2 2021-07-01 19:24:10
name3 10.0.0.3 2021-06-23 16:20:51
或者在 strftime() 中设置 UTC 标志,如果 UTC 是您的数据中的内容:
$ cat tst.awk
BEGIN { FS=",$9,1)
}
$ awk -f tst.awk file
name1 10.0.0.1 2021-06-23 12:51:08
name2 10.0.0.2 2021-07-01 19:24:10
name3 10.0.0.3 2021-06-23 16:20:51
,
也许不是最有效的 awk
实现,但在无法弄清楚 @anubhava 解决方案如何工作后,我想出了这个 hack。
awk -F"," '{ if (NR > 2 && NR < 6){ print $2,$8 }}' $file | sed -r 's/Mon|Tue|Wed|Thu|Fri|Sat|Sun//' | awk '{if ($4~/1/) $4="01"}{if ($3~/Jun/) $3="06" }{if ($3~/Jul/) $3="07"}{ print $1,$2,$6"-"$3"-"$4,$5}'
name1 10.0.0.1 2021-06-23 12:51:08
name2 10.0.0.2 2021-07-01 19:24:10
name3 10.0.0.3 2021-06-23 16:20:51