Awk 文本处理:基于另一列值偏移列中的日期值
我在第4列有一个日期值为YYYYMMDD格式的文本文件,在第5列有一个月偏移量Awk 文本处理:基于另一列值偏移列中的日期值,awk,Awk,我在第4列有一个日期值为YYYYMMDD格式的文本文件,在第5列有一个月偏移量 a1|b1|c1|20190101|1|1|11|A|D a1|b1|c1|20190101|2|2|12|B|E a1|b1|c1|20190101|3|3|13|C|F a2|b2|c2|20190101|1|4|14|G|J a2|b2|c2|20190101|2|5|15|H|K a2|b2|c2|20190101|3|6|16|I|L 我想将第5列中的值替换为 日期-d'(第4列中的值)-(第5列中的值)
a1|b1|c1|20190101|1|1|11|A|D
a1|b1|c1|20190101|2|2|12|B|E
a1|b1|c1|20190101|3|3|13|C|F
a2|b2|c2|20190101|1|4|14|G|J
a2|b2|c2|20190101|2|5|15|H|K
a2|b2|c2|20190101|3|6|16|I|L
我想将第5列中的值替换为
日期-d'(第4列中的值)-(第5列中的值)月+1个月'+'%Y%m%d'
所以最终结果应该如下所示
a1|b1|c1|20190101|20190101|1|11|A|D
a1|b1|c1|20190101|20181201|2|12|B|E
a1|b1|c1|20190101|20181101|3|13|C|F
a2|b2|c2|20190101|20190101|4|14|G|J
a2|b2|c2|20190101|20181201|5|15|H|K
a2|b2|c2|20190101|20181101|6|16|I|L
我用awk-f offsetMonths.awk打成平局
BEGIN{
FS="|"
OFS = FS
}
{
# Date field is in column 4, offset is in column 5
# Replace column 5 with the offset date
"date -d '"$4" -"$5" months +1 months' +'%Y%m%d' " | getline l
$5 = l
print $0
}
我得到的是
a1|b1|c1|20190101|20190101|1|11|A|D
a1|b1|c1|20190101|20181201|2|12|B|E
a1|b1|c1|20190101|20181101|3|13|C|F
a2|b2|c2|20190101|20181101|4|14|G|J
a2|b2|c2|20190101|20181101|5|15|H|K
a2|b2|c2|20190101|20181101|6|16|I|L
请注意,使用GNU awk第4行到第6行中的月偏移值不正确,任何日期转换都应使用提供的日期函数执行。此问题的两个有用时间函数是
mktime
和strftime
:
:将格式为mktime(datespec)
的日期规范字符串YYYY-MM-DD-hh-MM-ss
转换为Unix纪元时间,即自1970 01 UTC以来的总秒数。由于gawk-4.2.1,您可以使用datespec
指示utc标志
是否处于utcdatespec
:将历元时间strftime(格式,时间戳)
转换为格式化字符串(格式与时间戳
命令相同)。您可以使用日期
指示返回的时间应为utc或本地时区utc标志
convert.awk
BEGIN {FS=OFS="|"}
{ d=$4
time=mktime(substr(d,1,4)" "substr(d,5,2)+1-$5" "substr(d,7,2)" 00 00 00")
$5=strftime("%Y%m%d",time)
print
}' file
您可以通过以下方式运行此功能:
$ awk -f convert.awk file.txt
mktime
具有显著的相加性。因此,您传递的格式为YYYY-MM-DD-hh-MM-ss
的字符串不需要是正确的日期,您可以有不正确的值。例如,字符串2019 01 32 00 00
相当于2019 02 01 00 00
,2019 5-10 00 00
相当于2019 04 20 00 00
,甚至2019-19-10 00
相当于2017 04 20 00
注意:因为我们直接在
mktime
中修改时间,所以我们不需要担心夏令时(参见注释)。除了kvantour的答案之外(这可能比我的答案更简洁,因为我更喜欢使用awk内置函数,而不是在awk中调用bash命令),以下是您的代码修复:
BEGIN{
FS="|"
OFS = FS
}
{
# Date field is in column 4, offset is in column 5
# Replace column 5 with the offset date
cmd = "date -d '"$4" -"$5-1" months' +'%Y%m%d'" # as suggested by @kvantour
cmd | getline result
close(cmd)
$5 = result
print $0
}
结果:
a1|b1|c1|20190101|20190101|1|11|A|D
a1|b1|c1|20190101|20181201|2|12|B|E
a1|b1|c1|20190101|20181101|3|13|C|F
a2|b2|c2|20190101|20190101|4|14|G|J
a2|b2|c2|20190101|20181201|5|15|H|K
a2|b2|c2|20190101|20181101|6|16|I|L
这里有更多
在不调用close()的情况下,awk创建子进程来运行命令,直到最终耗尽更多管道的文件描述符
您不需要为此调用外部
date
实用程序或特定于gawk的时间函数,这只是数学:
$ cat tst.awk
BEGIN { FS=OFS="|" }
{
delta = $5 - 1
year = substr($4,1,4)
month = substr($4,5,2)
day = substr($4,7)
month = month - delta
if (month <= 0) {
year--
month += 12
}
$5 = sprintf("%04d%02d%02d",year,month,day)
print
}
$
$ awk -f tst.awk file
a1|b1|c1|20190101|20190101|1|11|A|D
a1|b1|c1|20190101|20181201|2|12|B|E
a1|b1|c1|20190101|20181101|3|13|C|F
a2|b2|c2|20190101|20190101|4|14|G|J
a2|b2|c2|20190101|20181201|5|15|H|K
a2|b2|c2|20190101|20181101|6|16|I|L
$cat tst.awk
开始{FS=OFS=“|”}
{
增量=$5-1
年份=次级资源(4,1,4美元)
月=子月($4,5,2)
日=分包(4.7美元)
月=月-增量
如果(月)我会明确提到关闭的原因。另外,你可以使用$5-1
而不是“-”$5”月+1个月。谢谢@kvantour,我引用了使用关闭的原因(而不是试图解释它并出错:p)它需要是$5=(cmd|getline result)>0?结果:“N/A”);关闭(cmd)
或类似于明智地处理日期/getline故障。请参见@EdMorton Aha。只有在通过减去秒来更改mktime
之外的时间时,这似乎才是危险的($TZ='Europe/Monaco'awk'BEGIN{t=mktime(“2019 05 01 00 00 00”);打印标准时间(“%FT%t”,t);打印标准时间(“%FT%t”,t-86400*120)
)首先,祝你好运。为了安全起见,month
可能大于12,同时,也可能是几年后。if(month>0){year+=int((month-1)/12);month=(month-1)%12+1};if(month<1){year-=int(month/12)-1;month=month%12+12}
是的,这是可能的。如果OP说这可能发生,我会更新。谢谢。