【问题标题】:Replace column in CSV file with one-minute interval timestamps用一分钟间隔时间戳替换 CSV 文件中的列
【发布时间】:2016-10-03 12:12:00
【问题描述】:

我有一个带有时间戳和一些数据值的 CSV 文件:

 1455840000,76.357,899.500,326.717,8.000
 1455840060,76.490,899.650,326.150,8.000
 ...etc

但我想用新的时间戳值替换时间戳列。最后一行应该是当前时间,并且所有之前的行都应该比它之后的行早一分钟。

如何使用 shell 脚本执行此操作,例如bash/awk?

【问题讨论】:

  • 我们如何知道startend 时间戳?我们从哪里开始和结束?
  • @Inian 结束时间戳是当前时间。然后在一分钟递减中以相反的顺序迭代到文件的开头。该文件可以是任意长度,因此我们不知道开始时间戳,除非通过倒数行数。
  • 当你说然后以相反的顺序迭代到文件的开头,什么定义了start

标签: bash awk


【解决方案1】:

你可以使用tac反转文件内容,做操作,然后反转回原来的顺序:

tac file.txt | \
    awk 'BEGIN{FS=OFS=","} NR==1{"date +%s"|getline cur; $1=cur; print; next}; \
              {$1=cur-(60*(NR-1)); print}' | tac
  • 对于(反转的)第一行,我们以epoch为单位获取当前时间戳,并将其保存在变量cur

  • 对于下一行,我们将每一行减去60 * (line number - 1) 秒以获得所需的时间

请注意,时间计算可能并不像您想象的那样精确。

示例:

% cat ts.txt 
1455840000,76.357,899.500,326.717,8.000
1455840060,76.490,899.650,326.150,8.000
1455840000,76.357,899.500,326.717,8.000
1455840060,76.490,899.650,326.150,8.000

% tac ts.txt | awk 'BEGIN{FS=OFS=","} NR==1{"date +%s"|getline cur; $1=cur; print; next}; {$1=cur-(60*(NR-1)); print}' | tac
1475497096,76.357,899.500,326.717,8.000
1475497156,76.490,899.650,326.150,8.000
1475497216,76.357,899.500,326.717,8.000
1475497276,76.490,899.650,326.150,8.000

【讨论】:

  • 根据我对 Aaron 回答的评论,Q 中没有迹象表明 OP 正在运行可能包含 tac 的操作系统,因此我提到了一些替代方案。
【解决方案2】:

这可能是你想要的:

$ cat file
1455840000,76.357,899.500,326.717,8.000
1455840060,76.490,899.650,326.150,8.000
1455840000,76.357,899.500,326.717,8.000
1455840060,76.490,899.650,326.150,8.000
1455840000,76.357,899.500,326.717,8.000
1455840060,76.490,899.650,326.150,8.000

使用 GNU awk:

$ awk 'BEGIN{FS=OFS=","; now=systime()} NR>FNR{$1 = now - (NR-2*FNR)*60; print}' file file
1475504973,76.357,899.500,326.717,8.000
1475505033,76.490,899.650,326.150,8.000
1475505093,76.357,899.500,326.717,8.000
1475505153,76.490,899.650,326.150,8.000
1475505213,76.357,899.500,326.717,8.000
1475505273,76.490,899.650,326.150,8.000

与其他 awks:

$ awk -v now=$(date +'%s') 'BEGIN{FS=OFS=","} NR>FNR{$1 = now - (NR-2*FNR)*60; print}' file file
1475504973,76.357,899.500,326.717,8.000
1475505033,76.490,899.650,326.150,8.000
1475505093,76.357,899.500,326.717,8.000
1475505153,76.490,899.650,326.150,8.000
1475505213,76.357,899.500,326.717,8.000
1475505273,76.490,899.650,326.150,8.000

【讨论】:

    【解决方案3】:

    我会这样做:

    tac inputFile | awk -v ts="$(date +%s)" -v OFS=, -F, '{ $4 = strftime("%c", ts - NR * 60) ; print $0 }' | tac
    

    tac 反转输入文件,以便我们可以从唯一已知的值(当前日期)计算日期。处理完每一行后,我们会将其还原。

    awk-v 标志使我们能够使用变量,因此我们让bash 计算当前时间戳并将其作为unix 时间戳(自01/01 以来的秒数)传递给awk /1970)。

    awk-F 标志指定列分隔符。

    然后在每一行,最后一列替换为给定的时间戳减去之前读取的每一行的 60 秒,我们用 strftime 以人类可读的格式显示。

    例子:

    $ cat inputFile
    a,b,c,d
    a1,b1,c1,d1
    a2,b2,c2,d2
    
    $ tac inputFile | awk -v ts="$(date +%s)" -v OFS=, -F, '{ $4 = strftime("%c", ts - NR * 60) ; print $0 }' | tac
    a,b,c,lun.  3 oct. 2016 15:32:29
    a1,b1,c1,lun.  3 oct. 2016 15:33:29
    a2,b2,c2,lun.  3 oct. 2016 15:34:29
    

    【讨论】:

    • 如果您使用的操作系统不包含 tac(如 FreeBSD 或 OS X 或 Solaris 或 AIX 等),请检查您的 tail 命令以获取 -r选项。如果失败,您可以使用awk '{L[i++]=$0} END {for(j=i-1;j>=0;)print L[j--]}' 获得类似的结果(假设您有存储文件的内存)。
    【解决方案4】:

    此 Gnu awk 脚本首先获取当前时间戳(纪元时间),在第一次迭代后记住文件的 NR 并在第二次迭代时更新时间戳:

    $ awk -F, 'BEGIN{ts=strftime("%s")} NR==FNR{nr=NR; next}{$1=ts-(nr-FNR)*60} 1' file file
    1455840000 76.357 899.500 326.717 8.000
    1455840060 76.490 899.650 326.150 8.000
    

    为了与所有 awk(包括 Gnu awk)兼容,请将上面的 BEGIN{} 块替换为

    BEGIN{"date +'%s'"|getline ts}

    【讨论】:

    • strftime() 函数是 GNU awk 的一部分,但不是我知道的其他变体。
    猜你喜欢
    • 2018-05-15
    • 1970-01-01
    • 1970-01-01
    • 2021-07-16
    • 2020-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-14
    相关资源
    最近更新 更多