[英]Insert a date in a column using awk
我正在嘗試在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
一個簡單的方法是
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
不會在其stdout中打印任何內容,因此getline
失敗並且$3
不變。
注意事項:
mktime
,用gawk的mktime
和strftime
手動解析日期。 編輯回復:注釋:要將選項卡用作分隔符,可以將命令更改為
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
。
這應該與您給定的輸入一起工作
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
我建議使用支持解析日期的語言,例如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
的標准格式說明符來解析和格式化日期。 如果第二個字段不是“ null”,則此解決方案將輸入拆分為制表符並修改格式。
這種方法比使用system
調用或調用子流程要快得多,因為一切都在本機perl中完成。
這是在純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
只能進行一次日期調用,並且沒有代碼注入問題,請參閱以下內容:
該腳本將日期提取(使用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"
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.