问题描述
有一个csv文件,其中的行有5个用逗号隔开的字段:
2020-07-31 15:15:55,xx,yy,zz,t
2020-07-31 15:16:57,t
2020-07-31 15:17:00,t
并且我想以秒为单位(相对时间)将第一行用作0,所以输出是这样的:
0,t
62,t
65,t
我可以使用任何编程语言来完成工作,例如bash,awk,sed,perl ...,然后覆盖同一文件或创建一个新文件。
解决方法
perl -MTime::Piece -F,-lane '
BEGIN {$,= ","}
$t = Time::Piece->strptime(shift(@F),"%F %T")->epoch;
if ($. == 1) {$start = $t}
print $t - $start,@F;
' file
,
这是UNIX环境无法轻松处理的问题。
使用teip
。
$ teip -d,-f1 -- sh -c "date -f- +%s" < file.txt |
awk -F,'NR==1{n=$1}{$1=$1-n};4' OFS=,xx,yy,zz,t
62,t
65,t
,
@anubhava的原始答案(只有2个字段):
您可以将此awk
与mktime
函数一起使用。
这将输出两个用逗号分隔的字段:
awk 'BEGIN {
FS=OFS="," # set input and output field separators to comma
}
{
gsub(/[-:]/," ",$1) # replace - and : with a space
tm = mktime($1) # convert date-time string to EPOCH value
if (NR == 1) # for 1st records store this value in first
first = tm
print (tm - first),$2 # print difference and 2nd field for each record
}' file
结果:
0,xx
62,xx
65,xx
由@smeterlink改进
这将使用NF变量检测所有逗号分隔的字段,因此即使只有第一个字段也可以使用。这样可以混合使用不同数量字段的行:
get.awk :
BEGIN {
FS=OFS=",$1) # replace - and : with a space
tm = mktime($1) # convert date-time string to EPOCH value
if (NR == 1) # for 1st records store this value in first
first = tm
{
printf (tm - first) # print difference
if (NF > 1) # print 2nd to last fields only if they exist
{
for (i = 2; i < NF; i += 1)
printf ","$i
print ","$NF
}
else
print "" # otherwise print newline after printf
}
}
file.csv :
2020-07-31 15:15:55,t
2020-07-31 15:16:57,t
2020-07-31 15:17:00,t
2020-07-31 15:17:23,abc,009-%5
2020-07-31 15:18:00
2020-07-31 15:19:00,xx
结果:
awk -f get.awk file.csv
0,t
88,009-%5
125
185,xx
,
这是一个可行的部分答案,考虑到日期不变,因此将其省略。
cat file.csv | awk -F ' ' '{print $2}'| awk -F ':|,' '{printf ($1*3600+$2*60+$3)}{print ","$4","$5","$6","$7}'
这将输出此结果,该结果仍保留到第一个值,可以从文件中读取:
54955,t
55017,t
55020,t
现在,由于所有行的值都相同,因此可以手动输入:
cat file.csv | awk -F ' ' '{print $2}'| awk -F ':|,' '{printf ($1*3600+$2*60+$3-54955)}{print ","$7}'
这将给出期望的结果:
0,t
*** 稍后添加:由于此线程提供了一种将时间转换为纪元的方法,这是绝对的: Convert date to epoch time using AWK in linux 所以它会像这样:
cat file.csv | awk -F,'{ OFS = FS;command="date -d " "\"" $1 "\"" " +%s";command | getline $1;close(command);print}'
1596201355,t
1596201417,t
1596201420,t
** 稍后再添加: 保留第一个值的方法是使用以下awk表达式:
| awk -F,'NR==1{pattern=$1}{printf ($1-pattern)}{print ","$2","$3","$5}'
因此,之前说明的2种方式将保持不变: 这种情况只能在同一天起作用:
cat file.csv | awk -F ' ' '{print $2}'| awk -F ':|,"$7}' | awk -F,"$5}'
这(使用纪元)将考虑以下日子:
cat file.csv | awk -F,'{ OFS = FS;command="date -d " "\"" $1 "\"" " +%s";command | getline $1;close(command);print}' | awk -F,"$5}'
到目前为止最好的解决方案:将考虑使用时代的日子,并且可以与awk的任何变体一起使用,包括BSD变体。它还可以检测到有多少个字段,即使只有第一个字段也可以使用:
cat file.csv | awk -F,'NR==1{pattern=$1}{printf ($1-pattern)}{if (NF > 1){for (i = 2; i < NF; i += 1) printf ","$i; print ","$NF} else print ""}'
file.csv :
2020-07-31 15:15:55,xx
结果:
0,xx