用命令输出替换文件的第一列

用命令输出替换文件的第一列

从这个问题使用 unix 复制并替换列

我尝试构建一个仅适用于此文件的解决方案:

20070101 10.2317  79.1638   6.0  26.7  20.9   0.8  14.0  98.6
20070102 10.2317  79.1638   5.6  26.5  20.8   1.9  13.6  98.0
20070103 10.2317  79.1638   7.5  27.7  20.8   0.1  15.8  96.4
20070104 10.2317  79.1638   8.1  26.0  19.6   0.0  15.5  94.1

并得到输出:

01/01/2007  10.2317   79.1638   6.0  26.7  20.9   0.8  14.0  98.6
02/01/2007  10.2317   79.1638   5.6  26.5  20.8   1.9  13.6  98.0
03/01/2007  10.2317   79.1638   7.5  27.7  20.8   0.1  15.8  96.4
04/01/2007  10.2317   79.1638   8.1  26.0  19.6   0.0  15.5  94.1

不涉及其他文件。

所以我想用转换后的日期替换第一列。

我从原始文件中获取了日期:

$ awk '{print $1}' filedate.txt
20070101
20070102
20070103
20070104

然后我用以下方法进行了日期转换:

for i in $(awk '{print $1}' filedate.txt); do date -d "$i"  +%d/%m/%Y; done
01/01/2007
02/01/2007
03/01/2007
04/01/2007

但我无法通过已转换的日期值修改文件的第一列。我尝试使用 awk 替换 ( awk '{$1=$dt}1'):

for i in $(awk '{print $1}' filedate.txt); do dt=$(date -d "$i" +%d/%m/%Y) && awk '{$1=$dt}1' filedate.txt; done

但由于涉及循环,输出不是所需的。

我怎样才能做到这一点awk?是否可以对 做同样的事情sed

编辑

在另一个问题的评论中我看到了以下方式sed

 sed 's,^\([0-9]\{4\}\)\([0-9]\{2\}\)\([0-9]\{2\}\),\3/\2/\1,'

但我现在很好奇如何执行涉及date命令的操作。

答案1

让我们使用您已经存在的循环(我还没有真正看过但它似乎可以完成它的工作):

for i in $(awk '{print $1}' filedate.txt); do date -d "$i"  +%d/%m/%Y; done

然后掖一下我对另一个问题的回答对其进行稍微修改:

for i in $(awk '{print $1}' filedate.txt); do date -d "$i"  +%d/%m/%Y; done |
paste - <( cut -d ' ' -f 2- filedate.txt )

结果:

01/01/2007      10.2317  79.1638   6.0  26.7  20.9   0.8  14.0  98.6
02/01/2007      10.2317  79.1638   5.6  26.5  20.8   1.9  13.6  98.0
03/01/2007      10.2317  79.1638   7.5  27.7  20.8   0.1  15.8  96.4
04/01/2007      10.2317  79.1638   8.1  26.0  19.6   0.0  15.5  94.1

更短,没有循环:

date -f <( cut -d ' ' -f 1 filedate.txt ) +"%d/%m/%Y" |
paste - <( cut -d ' ' -f 2- filedate.txt )

不带管道:

paste <( date -f <( cut -d ' ' -f 1 filedate.txt ) +"%d/%m/%Y" ) \
      <( cut -d ' ' -f 2- filedate.txt )

所有这些示例显然都需要 或bashksh任何其他能够理解进程替换的 shell。date还需要GNU 。

我对另一个问题的回答以获得其工作原理的解释。

答案2

如果您想要的转换只是对现有信息进行重新排序,为什么不这样做

awk '{ $1=sprintf("%02i/%02i/%04i",
     substr($1, 7, 2), substr($1, 5, 2), substr($1, 1, 4)) }1' file

我们从第一个字段中提取子字符串并将它们重新组装成第一个字段的新值,然后像往常一样打印整个输入行。 (右大括号后的单独字符1是用于无条件打印的标准 Awk 习惯用法。)

您可能会后悔将完美的机器可读日期转换为“人类可读”不过很快。

答案3

如果您有 GNU awk ( gawk),您可以使用命令输出替换列getline/变量/管道形式ofgetline来调用date函数:

gawk '{"date +%d/%m/%Y -d" $1 | getline $1} 1' file

但是,如果您只想更改列的日期格式,则可以使用内部函数mktimestrftime函数本地执行此操作:

gawk '{
  d = sprintf("%d %02d %02d 0 0 0", substr($1,1,4), substr($1,5,2), substr($1,7,2));
  t = mktime(d);
  $1 = strftime("%d/%m/%Y", t);
  } 1' file

尽管在这种情况下,您可以使用简单的字符串操作来完成所需的转换(这应该适用于任何风格awk):

$ mawk '{$1 = sprintf("%02d/%02d/%02d", substr($1,7,2), substr($1,5,2), substr($1,1,4))} 1' file
01/01/2007 10.2317 79.1638 6.0 26.7 20.9 0.8 14.0 98.6
02/01/2007 10.2317 79.1638 5.6 26.5 20.8 1.9 13.6 98.0
03/01/2007 10.2317 79.1638 7.5 27.7 20.8 0.1 15.8 96.4
04/01/2007 10.2317 79.1638 8.1 26.0 19.6 0.0 15.5 94.1

答案4

GNU sed 命令的eval 修饰符s将允许您date根据需要转换日期:

sed -r 's|(\S+)(.*)|date -d \1 "+%d/%m/%y \2"|e'

替换的表达式是格式正确的日期命令。修饰符e导致对每一行执行此命令,并且模式缓冲区(以及输出)将被每个日期命令的输出替换。

相关内容