我有一个包含以下数据结构的 csv:
1111,2222,3333,4444,5555,6666,7777,2017-1-5 1:07:09,2017-1-5 1:11:53
1111,2222,3333,4444,5555,6666,7777,2017-11-25 19:57:17,2017-11-25 19:58:54
我想显示日期、月和日,一如既往地为 2 位数字长。我还希望时间 Hour 字段始终为 2 位数字。
如果月/日/小时字段仅为单个数字(如上面的示例行所示),则本质上是添加前导零。
使用 awk,我将如何实现以下结果:
1111,2222,3333,4444,5555,6666,7777,2017-01-05 01:07:09,2017-01-05 01:11:53
1111,2222,3333,4444,5555,6666,7777,2017-11-25 19:57:17,2017-11-25 19:58:54
答案1
一个很棒的文本处理工具是awk。以下示例在 FreeBSD 11.1 上使用普通标准 awk。如果您更喜欢 GNU awk,@RomanPerekhrest 在另一个答案中有一个优雅的解决方案。
您的输入以逗号分隔。因此,我们awk
使用参数进行调用-F,
。
然后我们可以使用该语句打印出列print
。$1
是第一列。$2
是第二列。
$ awk -F, '{ print $8 }' inputfile.csv
2017-1-5 1:07:09
2017-11-25 19:57:17
这为我们提供了每行的第 8 列。
这就是您要操作的日期字段。我们可以将其作为脚本的一部分来设置,而不是使用命令行参数来设置分隔符。 FS 用于输入分隔符,OFS 用于输出分隔符。
$ awk 'BEGIN { FS = "," } ; { print $8 }' inputfile.csv
2017-1-5 1:07:09
2017-11-25 19:57:17
在处理日期时,我通常更喜欢使用date
util 来确保正确处理它们。我不需要担心我使用的是常规的还是 GNU awk。此外,如果日期解析不正确,我会遇到很大的失败。
有趣的参数是:
-j Specify we do not want to set the date at all
-f The format string we use for input
+ The format string we use for output
因此,如果我们运行这个日期:
$ date -j -f "%Y-%m-%d %H:%M:%S" +"%Y-%m-%d %H:%M:%S" "2017-1-5 1:07:09"
2017-01-05 01:07:09
然后我们可以将其与 awk 结合起来。注意引号是怎样的逃脱了。这可能是初学者最大的绊脚石。
$ awk -F, '{ system("date -j -f \"%Y-%m-%d %H:%M:%S\" +\"%Y-%m-%d %H:%M:%S\" \""$8"\"")}' inputfile.csv
2017-01-05 01:07:09
2017-11-25 19:57:17
系统调用似乎是正确的 - 但不幸的是它只允许我们捕获返回码并直接打印到输出。为了避免这种情况,我们使用该cmd | getline
模式。以下简单示例将把当前日期读入 mydate:
$ awk 'BEGIN { cmd = "date"; cmd | getline mydate; close(cmd); print mydate }'
Thu Mar 1 16:26:15 CET 2018
我们使用BEGIN
关键字是因为我们没有对此简单示例的输入。
那么让我们扩展一下:
awk 'BEGIN { FS=","; OFS=FS };
{
cmd = "date -j -f \"%Y-%m-%d %H:%M:%S\" +\"%Y-%m-%d %H:%M:%S\" \""$8"\"";
cmd | getline firstdate;
close(cmd);
cmd = "date -j -f \"%Y-%m-%d %H:%M:%S\" +\"%Y-%m-%d %H:%M:%S\" \""$9"\"";
cmd | getline seconddate;
close(cmd);
print $1,$2,$3,$4,$5,$6,$7,firstdate,seconddate
}' inputfile.csv
我们可以将其折叠成一行:
awk 'BEGIN {FS=",";OFS=FS};{cmd="date -j -f \"%Y-%m-%d %H:%M:%S\" +\"%Y-%m-%d %H:%M:%S\" \""$8"\"";cmd | getline firstdate;close(cmd);cmd="date -j -f \"%Y-%m-%d %H:%M:%S\" +\"%Y-%m-%d %H:%M:%S\" \""$9"\"";cmd | getline seconddate;close(cmd);print $1,$2,$3,$4,$5,$6,$7,firstdate,seconddate}' inputfile.csv
这给了我输出:
1111,2222,3333,4444,5555,6666,7777,2017-01-05 01:07:09,2017-01-05 01:11:53
1111,2222,3333,4444,5555,6666,7777,2017-11-25 19:57:17,2017-11-25 19:58:54
附录
由于这里的目的是学习好习惯,我最好更新这个答案。重复代码是一个坏习惯。当你开始这样做时,你应该将事情分成一个函数。您会注意到下面的代码立即变得更具可读性。
awk 'function convertdate(the_date) {
cmd = "date -j -f \"%Y-%m-%d %H:%M:%S\" +\"%Y-%m-%d %H:%M:%S\" \""the_date"\"";
cmd | getline formatted_date;
close(cmd);
return formatted_date
}
BEGIN { FS=","; OFS=FS };
{
print $1,$2,$3,$4,$5,$6,$7,convertdate($8),convertdate($9)
}' inputfile.csv
养成这个习惯,您会发现稍后引入错误处理会变得多么容易。
答案2
如果你有 GNU awk,你可以将最后一个字段转换为空格分隔的字段日期规范字符串,然后使用以下命令根据需要重新格式化它strftime
:
awk 'BEGIN{OFS=FS=","} {gsub(/[-:]/," ",$NF); $NF = strftime("%Y-%m-%d %H:%M:%S", mktime($NF))} 1' file
1111,2222,3333,4444,5555,6666,7777,2017-1-5 1:07:09,2017-01-05 01:11:53
1111,2222,3333,4444,5555,6666,7777,2017-11-25 19:57:17,2017-11-25 19:58:54
答案3
简单的 GNUawk
解决方案:
awk 'BEGIN{ FS=OFS="," }{ gsub(/\<[0-9]\>/, "0&", $8); gsub(/\<[0-9]\>/, "0&", $9) }1' file
gsub(/\<[0-9]\>/, "0&", <field>)
- 仅替换/补充其中的独立个位数约会时间细绳:\<
和\>
- 是单词边界&
- 代表正则表达式模式匹配的精确子字符串
输出:
1111,2222,3333,4444,5555,6666,7777,2017-01-05 01:07:09,2017-01-05 01:11:53
1111,2222,3333,4444,5555,6666,7777,2017-11-25 19:57:17,2017-11-25 19:58:54
答案4
这日期工具包中有处理时间/日期格式数据的详细信息的代码。
# Utility functions: print-as-echo, print-line-with-visual-space.
pe() { for _i;do printf "%s" "$_i";done; printf "\n"; }
pl() { pe;pe "-----" ;pe "$*"; }
pl " Input data file $FILE:"
head $FILE
pl " Expected output:"
cat $E
pl " Results, to standard format:"
dateutils.dconv -S <$FILE
pl " Results, to standard format, omitting the \"T\":"
dateutils.dconv -S -f '%F %T' <$FILE
生产:
-----
Input data file data1:
1111,2222,3333,4444,5555,6666,7777,2017-1-5 1:07:09,2017-1-5 1:11:53
1111,2222,3333,4444,5555,6666,7777,2017-11-25 19:57:17,2017-11-25 19:58:54
-----
Expected output:
1111,2222,3333,4444,5555,6666,7777,2017-01-05 01:07:09,2017-01-05 01:11:53
1111,2222,3333,4444,5555,6666,7777,2017-11-25 19:57:17,2017-11-25 19:58:54
-----
Results, to standard format:
1111,2222,3333,4444,5555,6666,7777,2017-01-05T01:07:09,2017-01-05T01:11:53
1111,2222,3333,4444,5555,6666,7777,2017-11-25T19:57:17,2017-11-25T19:58:54
-----
Results, to standard format, omitting the "T":
1111,2222,3333,4444,5555,6666,7777,2017-01-05 01:07:09,2017-01-05 01:11:53
1111,2222,3333,4444,5555,6666,7777,2017-11-25 19:57:17,2017-11-25 19:58:54
在这样的系统上:
OS, ker|rel, machine: Linux, 3.16.0-4-amd64, x86_64
Distribution : Debian 8.9 (jessie)
bash GNU bash 4.3.30
dateutils.dconv dconv 0.3.1
dconv 的一些详细信息:
dateutils.dconv Convert DATE/TIMEs between calendrical systems. (man)
Path : /usr/bin/dateutils.dconv
Package : dateutils
Home : http://www.fresse.org/dateutils
Version : 0.3.1
Type : ELF 64-bit LSB shared object, x86-64, version 1 ( ...)
Help : probably available with -h,--help
Home : https://github.com/hroptatyr/dateutils (doc)
最美好的祝愿...干杯,drl