在文本文件中插入或更新一行

Insert or update a row in a text file

我想将一个值写入带有时间戳的文本文件。 data/value 每隔几分钟更新一次,但我只想记录最后一个值及其日期。第二天,我想重新开始换行,但要保留以前的值。然后将输出图表显示每日值。

我可以用 if, then, else 循环来做到这一点,但我想还有更优雅的 awk、sed、'something' 解决方案。

这将由 cron 作业 运行。

第 1 天。 每 5 分钟插入一行 $date, $value

在同一天继续更新该行

第 2 天 插入新行并重复

期望输出

第 1 天上午 10 点

2021-08-17, 5.20

第 1 天晚上 11 点

2021-08-17, 12.10

第 2 天上午 10 点

2021-08-17, 12.10
2021-08-18, 4.90

第 2 天晚上 11 点

2021-08-17, 12.10
2021-08-18, 13.10

使用两个不同的 cronjob 条目。在每天开始时运行的一个,它使用 ed 或其他任何方式向文件添加新行:

# Something that sets date and value here
printf "%s\n" '$a' "$date, $value" . w | ed -s /your/file.txt

另一个每 5 分钟运行一次并用新值替换文件的当前最后一行:

# Something that sets date and value here
printf "%s\n" '$c' "$date, $value" . w | ed -s /your/file.txt

一个想法是 skip/ignore/delete 包含今天日期的行(如果存在),然后附加一个包含今天日期的新行。

示例数据文件:

$ cat date.log
2021-08-14, 23.1
2021-08-15, 17.3
2021-08-16, 9.3

$ today=$(date '+%Y-%m-%d')
$ echo $today
2021-08-17

$ newvalue=13.2

实现此逻辑的一个 sed 想法:

$ sed -i -n -e "/^${today}, .*$/"'!p' -e '$a'"${today}, ${newvalue}" date.log

其中:

  • -i -n -e - -inplace更新源文件,-n禁止自动打印patternspace,-e指定一段脚本
  • "/^${today}, .*$/" - 搜索匹配的模式(行首)+ ${today} + , + 行的其余部分;需要使用双引号,以便 ${today} 被替换为它的实际值
  • '!p' - 反向模式搜索和打印行(即,打印除匹配 ^${today}, .*$ 的行之外的所有内容);需要使用单引号,因为双引号中的 !p 将替换为以字母 p
  • 开头的最后一个历史命令
  • -e '$a' - 找到文件结尾 ($) 和 a 追加以下字符串的另一段脚本;必须使用单引号,这样 bash 就不会尝试用变量 a
  • 的内容替换文字 $a
  • "${today}, ${newvalue}" - 要附加到文件末尾的字符串

如果我们在 sed 调用前加上 set -xv(启用调试模式),我们会在控制台看到以下内容:

+ sed -i -n -e '/^2021-08-17, .*$/!p' -e '$a2021-08-17, 13.2' date.log

我们文件的内容:

$ cat date.log
2021-08-14, 23.1
2021-08-15, 17.3
2021-08-16, 9.3
2021-08-17, 13.2              # new line appended to file

再运行几次(在发出 set +xv 以禁用调试模式后):

$ newvalue=32.7
$ sed -i -n -e "/^${today}, .*$/"'!p' -e '$a'"${today}, ${newvalue}" date.log
$ cat date.log
2021-08-14, 23.1
2021-08-15, 17.3
2021-08-16, 9.3
2021-08-17, 32.7              # updated (really deleted and appended)

$ newvalue=73.xxx
$ sed -i -n -e "/^${today}, .*$/"'!p' -e '$a'"${today}, ${newvalue}" date.log
$ cat date.log
2021-08-14, 23.1
2021-08-15, 17.3
2021-08-16, 9.3
2021-08-17, 73.xxx            # updated (really deleted and appended)

$ today='2021-09-23'
$ newvalue=7.25
$ sed -i -n -e "/^${today}, .*$/"'!p' -e '$a'"${today}, ${newvalue}" date.log
$ cat date.log
2021-08-14, 23.1
2021-08-15, 17.3
2021-08-16, 9.3
2021-08-17, 73.xxx
2021-09-23, 7.25              # new line appended to file

使用 GNU awk 的 -i inplace capable version

$ gawk -i inplace -v d=2021-08-17 -v v=12.20 '    # new data in parameters
@load "filefuncs"                                 # for stat to detect missing file
BEGIN {
    if(ARGC==1)                                   # if no file given
        exit 1                                    # exit with an error
    FS=OFS=", "                                   # set the field separators
    # d=strftime("%F")                            # no need parameterize the date
    if(stat(ARGV[1],fdata)<0) {                   # if given file does not exist
        print d,v > ARGV[1]                       # create it
        exit                                      # and exit
    }
}
{
    if(NR>1)                                  
        print pd,pv                               # print previous data record
    pd=                                         # store current for next round
    pv=
}
ENDFILE {                                         # in the end
    if(pd!=d)                                     # if dates differ
        print pd,pv                               # print previous
    print d,v                                     # print given data
    exit                                          # to skip over following files
}' file