我正在尝试格式化csv列中的日期。
输入类似于:28 April 1966
我想要这个输出:1966-04-28
可以使用以下代码获得:
date -d "28 April 1966" +%F
所以现在我想混合使用awk和这段代码来格式化整个列,但是我找不到如何。
编辑:
输入示例:(分隔符“|”实际上是制表符)
1 | 28 April 1966
2 | null
3 | null
4 | 30 June 1987
预期产出:
1 | 1966-04-28
2 | null
3 | null
4 | 30 June 1987
答案 0 :(得分:3)
一种简单的方法是
{
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
失败且mktime
未更改。
需要考虑的注意事项:
strftime
和awk -F '\t' -v OFS='\t' '{ cmd = "date -d \"" $3 "\" +%F 2> /dev/null"; cmd | getline $3; close(cmd) } 1' filename
手动解析日期。编辑:评论:要将标签用作分隔符,可以将命令更改为
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
编辑重新:评论2:如果性能令人担忧,就像看起来那样,每行产生流程都不是一个好方法。在这种情况下,您必须手动进行解析。例如:
foo.awk
将其放入文件中,例如awk -F '\t' -f foo.awk filename.csv
,然后运行auto ptr = static_cast<
typename _DescriptorType::FeatureType (*)(const Instance &, const _DescriptorType &)
>(getDescriptor<_DescriptorType>);
。
答案 1 :(得分:1)
这应该适用于您的输入
awk -F'\\|' -vOFS="|" '!/null/{cmd="date -d \""$3"\" +%F";cmd | getline $3;close(cmd)}1' file
| 1 |1966-04-28
| 2 | null
| 3 | null
| 4 |1987-06-30
答案 2 :(得分:1)
我建议使用支持解析日期的语言,例如perl:
$ cat file
1 28 April 1966
2 null
3 null
4 30 June 1987
$ perl -F'\t' -MTime::Piece -lane 'print "$F[0]\t",
$F[1] eq "null" ? $F[1] : Time::Piece->strptime($F[1], "%d %B %Y")->strftime("%F")' file
1 1966-04-28
2 null
3 null
4 1987-06-30
Time::Piece
核心模块允许您使用strftime
的标准格式说明符来解析和格式化日期。此解决方案将选项卡分为选项卡字符,如果第二个字段不是&#34; null&#34;则修改格式。
这种方法比使用system
调用或调用子进程要快得多,因为一切都是在本机perl中完成的。
答案 3 :(得分:0)
以下是如何在纯BASH中执行此操作,并避免从awk调用system
或getline
:
while IFS=$'\t' read -ra arr; do
[[ ${arr[1]} != "null" ]] && arr[1]=$(date -d "${arr[1]}" +%F)
printf "%s\t%s\n" "${arr[0]}" "${arr[1]}"
done < file
1 1966-04-28
2 null
3 null
4 1987-06-30
答案 4 :(得分:0)
只有一次日期调用且无代码注入问题,请参阅以下内容:
此脚本将日期(使用awk)提取到临时文件中,通过一次“日期”调用处理它们并将结果合并(使用awk)。
awk -F '\t' 'match($3,/null/) { $3 = "0000-01-01" } { print $3 }' input > temp.$$
date --file=temp.$$ +%F > dates.$$
awk -F '\t' -v OFS='\t' 'BEGIN {
while ( getline < "'"dates.$$"'" > 0 )
{
f1_counter++
if ($0 == "0000-01-01") {$0 = "null"}
date[f1_counter] = $0
}
}
{$3 = date[NR]}
1' input.$$
使用bash进程重定向单线(无临时文件):
inputfile=/path/to/input
awk -F '\t' -v OFS='\t' 'BEGIN {while ( getline < "'<(date -f <(awk -F '\t' 'match($3,/null/) { $3 = "0000-01-01" } { print $3 }' "$inputfile") +%F)'" > 0 ){f1_counter++; if ($0 == "0000-01-01") {$0 = "null"}; date[f1_counter] = $0}}{$3 = date[NR]}1' "$inputfile"
以下是它的使用方法:
# configuration
input=/path/to/input
temp1=temp.$$
temp2=dates.$$
output=output.$$
# create the sample file (optional)
#printf "\t%s\n" $'1\t28 April 1966' $'2\tnull' $'3\tnull' $'4\t30 June 1987' > "$input"
# Extract all dates
awk -F '\t' 'match($3,/null/) { $3 = "0000-01-01" } { print $3 }' "$input" > "$temp1"
# transform the dates
date --file="$temp1" +%F > "$temp2"
# merge csv with transformed date
awk -F '\t' -v OFS='\t' 'BEGIN {while ( getline < "'"$temp2"'" > 0 ){f1_counter++; if ($0 == "0000-01-01") {$0 = "null"}; date[f1_counter] = $0}}{$3 = date[NR]}1' "$input" > "$output"
# print the output
cat "$output"
# cleanup
rm "$temp1" "$temp2" "$output"
#rm "$input"