一个简单的方法是
awk -F '\\| ' -v OFS='| ' '{ cmd = "date -d \"" $3 "\" +%F 2> /dev/null"; cmd | getline $3; close(cmd) } 1' filename
即:
{
cmd = "date -d \"" $3 "\" +%F 2> /dev/null" # build shell command
cmd | getline $3 # run, capture output
close(cmd) # close pipe
}
1 # print
这是有效的,因为如果日期无效,date 不会在其标准输出中打印任何内容,因此getline 失败并且$3 不会更改。
注意事项:
- 对于非常大的文件,这将在这些 shell 中生成大量 shell 和进程(每行一个)。这可能会显着拖累性能。
-
小心代码注入。如果 CSV 文件来自不可靠的来源,这种方法很难防御攻击者,您最好还是走很长的路,手动解析日期使用 gawk 的
mktime 和 strftime。
EDIT re:comment:要将制表符用作分隔符,可以将命令更改为
awk -F '\t' -v OFS='\t' '{ cmd = "date -d \"" $3 "\" +%F 2> /dev/null"; cmd | getline $3; close(cmd) } 1' filename
编辑回复:评论 2: 如果性能令人担忧,那么为每一行生成进程并不是一个好方法。在这种情况下,您必须手动进行解析。例如:
BEGIN {
OFS = FS
m["January" ] = 1
m["February" ] = 2
m["March" ] = 3
m["April" ] = 4
m["May" ] = 5
m["June" ] = 6
m["July" ] = 7
m["August" ] = 8
m["September"] = 9
m["October" ] = 10
m["November" ] = 11
m["December" ] = 12
}
$3 !~ /null/ {
split($3, a, " ")
$3 = sprintf("%04d-%02d-%02d", a[3], m[a[2]], a[1])
}
1
把它放在一个文件中,比如foo.awk,然后运行awk -F '\t' -f foo.awk filename.csv。