假设我有一个如下文件:
1,aaa,2016-12-01 01:02:03 EST,bbb
2,ccc,2016-12-02 04:05:06 CST,ddd
3,eee,2016-12-03 07:08:09 EST,fff
我想添加第5个字段,它是字段3中的时间戳,但已转换为UTC。
这可以调用对date
或Linux中的任何东西的调用。我并不太担心性能,因为它只在少量文件中被调用一次。
我似乎就是想不出最好的办法。awk的strftime
没有接收到时区字段,所以我不知道使用它的最佳方式
使用Bash循环:
while IFS=, read -r -a linearr; do
printf '%s,' "${linearr[@]}"
date +'%F %T %Z' -u -d "${linearr[2]}"
done < infile
这导致
1,aaa,2016-12-01 01:02:03 EST,bbb,2016-12-01 06:02:03 UTC
2,ccc,2016-12-02 04:05:06 CST,ddd,2016-12-02 10:05:06 UTC
3,eee,2016-12-03 07:08:09 EST,fff,2016-12-03 12:08:09 UTC
这会将每一行读取到数组linearr
中,打印末尾添加逗号的行,然后附加新的日期字符串。
或者,使用paste
和cut
:
paste -d, infile <(while read line; do date +'%F %T %Z' -u -d "$line"; done < <(cut -d , -f 3 infile))
或者,可读性更强:
paste -d ,
infile
<(while read line; do
date +'%F %T %Z' -u -d "$line"
done < <(cut -d, -f3 infile)
)
请注意,date
的-d
选项是GNUdate
特有的。POSIXdate
没有使用当前系统日期以外的日期的选项,而FreeBSD中的date
使用了另一个选项-r
,它期望"自Epoch以来的秒数"作为其参数。
使用GNU awk,可以通过操作TZ
环境变量来实现这一点。当您只处理时间偏移时,格式有点糟糕,但如果您有指定时区的std字符串,那么它就很简单了。
在awk中,环境存储在数组ENVIRON
中。ENVIRON
的修改是实现定义的:
ENVIRON
:表示环境值的数组,如POSIX.1-2017的系统接口卷中定义的exec函数中所述。数组的索引应为包含环境变量名称的字符串,每个数组元素的值应为包含该变量值的字符串。如果合适,环境变量应被视为数字字符串(参见awk中的表达式);数组元素也应具有其数值。在awk的行为受到环境变量影响的所有情况下(包括awk通过系统函数或通过print语句、
printf
语句或getline
函数的管道重定向执行的任何命令的环境),所使用的环境应为awk开始执行时的环境实现定义了ENVIRON的任何修改是否会影响此环境来源:POSIX.1-2017
GNU awk,另一方面,声明如下:
然而,从4.2版本开始,如果不处于POSIX兼容模式,gawk会在
ENVIRON
更改时更新自己的环境,从而更改它创建的程序所看到的环境。
所以现在可以通过做类似这样的事情来利用它:类似的事情
ENVIRON["TZ"] = std offset
以下是几个例子:
ENVIRON["TZ"] = "UTC"
ENVIRON["TZ"] = "UTC+03:00"
ENVIRON["TZ"] = "CET"
关于OP,我们可以这样做:
awk 'BEGIN{FS=OFS=","}
{time=$2; gsub(/[^0-9]/," ",time); tz=$2; gsub(/^.* /,"",tz)}
{ENVIRON["TZ"]=tz; print $0,strftime("%F %T",mktime(time),1)}
' file
注意:对于CSV文件,您应该使用What';使用awk高效解析CSV的最稳健方法是什么?
注:遗憾的是,并不是所有已知形式的TZ定义都得到认可。