如何在Linux中使用脚本将具有相同时间的时间序列数据中的多行合并为一条记录



我有一个时间序列数据,其中来自不同传感器的测量值被异步捕获并连接到同一个ascii文件中。传感器值已在同一时间实例中捕获。

这些值以空格分隔。

原始文件如下所示。

2022 281 08 48 14 876 10                1.00       NOTSAMPLED          NOTSAMPLED
2022 281 08 48 14 876 10          NOTSAMPLED             0.00          NOTSAMPLED
2022 281 08 48 14 876 10          NOTSAMPLED       NOTSAMPLED                1.00
2022 281 08 48 15 391 11                1.00       NOTSAMPLED          NOTSAMPLED
2022 281 08 48 15 391 11          NOTSAMPLED             0.00          NOTSAMPLED
2022 281 08 48 15 391 11          NOTSAMPLED       NOTSAMPLED                1.00
2022 281 08 48 15 896 12                1.00       NOTSAMPLED          NOTSAMPLED
2022 281 08 48 15 896 12          NOTSAMPLED             0.00          NOTSAMPLED
2022 281 08 48 15 896 12          NOTSAMPLED       NOTSAMPLED                1.00

现在,我需要用下面提到的上一个实例传感器值替换字符串NOTSAMPLED,并将分布在多行和多列中的传感器值合并为具有相同时间的单行。

2022 281 08 48 14 876 10                1.00       0.0     1.0
2022 281 08 48 15 391 11                1.00       0.0     1.0
2022 281 08 48 15 896 12                1.00       0.0     1.0

类似地,如果输入数据是

2022 281 08 48 14 876 10                1.00       NOTSAMPLED          NOTSAMPLED
2022 281 08 48 14 876 10          NOTSAMPLED             0.00          NOTSAMPLED
2022 281 08 48 14 880 10          NOTSAMPLED       NOTSAMPLED               10.00
2022 281 08 48 15 391 11                1.00       NOTSAMPLED          NOTSAMPLED
2022 281 08 48 15 391 11          NOTSAMPLED             0.00          NOTSAMPLED
2022 281 08 48 15 395 11          NOTSAMPLED       NOTSAMPLED               11.00
2022 281 08 48 15 896 12                1.00       NOTSAMPLED          NOTSAMPLED
2022 281 08 48 15 896 12          NOTSAMPLED             0.00          NOTSAMPLED
2022 281 08 48 15 900 12          NOTSAMPLED       NOTSAMPLED               12.00

那么我的预期输出是

2022 281 08 48 14 876 10                1.00             0.00          NOTSAMPLED
2022 281 08 48 14 880 10                1.00             0.00               10.00
2022 281 08 48 15 391 11                1.00             0.00               10.00
2022 281 08 48 15 395 11                1.00             0.00               11.00
2022 281 08 48 15 896 12                1.00             0.00               11.00
2022 281 08 48 15 900 12                1.00             0.00               12.00

如何使用sed/awk或任何其他bash-shell脚本命令来实现它?

我尝试了以下脚本。

#! /bin/bash
inp_filename=$1
awk '
NR == 1 { split($0, filldown) }
{
for (i = 6; i <= NF; i++)
if ($i != "NOTSAMPLED")
filldown[i] = $i
else
$i = filldown[i]
print
}
' $inp_filename`

但结果是

2022 281 08 48 14 876 10 1.00 NOTSAMPLED NOTSAMPLED
2022 281 08 48 14 876 10 1.00 0.00 NOTSAMPLED
2022 281 08 48 14 876 10 1.00 0.00 1.00
2022 281 08 48 15 391 11 1.00 0.00 NOTSAMPLED
2022 281 08 48 15 391 11 1.00 0.00 NOTSAMPLED
2022 281 08 48 15 391 11 1.00 0.00 1.00
2022 281 08 48 15 896 12 1.00 0.00 NOTSAMPLED
2022 281 08 48 15 896 12 1.00 0.00 NOTSAMPLED
2022 281 08 48 15 896 12 1.00 0.00 1.00

一个awk想法:

awk '
function print_values(  j) {
if (FNR>1) {
printf "%s", ts_prev                # print previous date/time stamp
for (j=8;j<=NF;j++)                 # loop through sensor values and append to current line of output
printf "%s%s", OFS, (values[j]=="" ? "NOTSAMPLED" : values[j])
print ""                            # terminate current line of output
}
ts_prev = ts_curr
}
{ gsub(/r/,"")                         # per comment from OP, need to remove windows/dow line endings
ts_curr=$1                            # save date/time stamp of current line
for (i=2;i<=7;i++)
ts_curr = ts_curr FS $i
if (ts_curr != ts_prev)               # if this is a new date/time stamp then ...
print_values()                    # print previous date/time stamp and associated sensor values
for (i=8;i<=NF;i++)                   # loop through values and ...
if ($i != "NOTSAMPLED")           # if a valid value then ...
values[i]=$i                   # save the value
}
END { print_values() }                      # flush last date/time stamp to stdout
' sensor.dat

这将生成:

2022 281 08 48 14 876 10 1.00 0.00 NOTSAMPLED
2022 281 08 48 14 880 10 1.00 0.00 10.00
2022 281 08 48 15 391 11 1.00 0.00 10.00
2022 281 08 48 15 395 11 1.00 0.00 11.00
2022 281 08 48 15 896 12 1.00 0.00 11.00
2022 281 08 48 15 900 12 1.00 0.00 12.00

这将填充"NOTSAMPLED";具有前一行中同一列的有效采样值。前几(3(行保持不变,因为之前没有采样值。

awk -v ns=NOTSAMPLED '
BEGIN {a[0]=ns; a[1]=ns; a[2]=ns}
{
for (i=0; i<3; ++i) {
if ($(NF-i) == ns) {
$(NF-i) = a[2-i]
}
else {
a[2-i] = $(NF-i)
}
}
print
}' myfile

这将用最近的前一个值填充没有的字段(靠近开始(。它解析文件两次——一次获得前三个采样值,另一次以与上一个示例相同的方式填充。

awk -v n="NOTSAMPLED" '
FNR==NR && filled != 3 {
for (i=0; i<3; ++i) {
if ($(NF-i) != n && a[2-i] == "") {
a[2-i] = $(NF-i)
++filled
}
}
nextline
}
FNR!=NR {
for (i=0; i<3; ++i) {
if ($(NF-i) == n) {
$(NF-i) = a[2-i]
}
else {
a[2-i] = $(NF-i)
}
}
print
}' myfile myfile

输出用一个空格分隔所有列。如果您想要更长的空白,只需使用适当的格式字符串(如printf "...%20s%20s%20sn", ...,$8,$9,$10(将print更改为printf

说明:

  • 扫描每一行,为每一列保留一个最新有效样本的数组。

  • NOTSAMPLED替换为数组中的值。如果字段有效的,请更新相应的数组元素。

最新更新