使用 Moreutils ts 进行管道

使用 Moreutils ts 进行管道

我在串行端口有一个传入流,每秒大约出现一次新行

wren@Raven:~$ cat /dev/ttyUSB0

A_Sensor1,B_22.00,C_50.00

A_Sensor1,B_22.00,C_50.00

A_Sensor1,B_22.00,C_50.00

A_Sensor1,B_22.00,C_50.00

A_Sensor1,B_22.00,C_50.00

我想删除空白行并为剩余部分添加时间戳。

sed 将剔除空白行并添加时间戳,但我无法更新时间戳,它只报告调用的时间:

wren@Raven:~$ cat /dev/ttyUSB0 | sed -e '/^$/d' -e "s/$/`date +\,%F\,%T`/"
A_Sensor1,B_22.00,C_50.00,2014-05-14,09:44:42
A_Sensor1,B_22.00,C_50.00,2014-05-14,09:44:42
A_Sensor1,B_22.00,C_50.00,2014-05-14,09:44:42
A_Sensor1,B_22.00,C_50.00,2014-05-14,09:44:42
A_Sensor1,B_22.00,C_50.00,2014-05-14,09:44:42
A_Sensor1,B_22.00,C_50.00,2014-05-14,09:44:42
A_Sensor1,B_22.00,C_50.00,2014-05-14,09:44:42
^C

我找到了 ts,它是 Moreutils 的一部分,可以通过管道传输到它来获取更新时间戳。

wren@Raven:~$ cat /dev/ttyUSB0 |  ts
May 14 09:49:26 A_Sensor1,B_22.00,C_50.00
May 14 09:49:26
May 14 09:49:27 A_Sensor1,B_22.00,C_50.00
^C

但是,我无法正确地将 ts 与 sed 结合起来。

这看起来应该可以做我想要的事情,但却没有产生任何输出

wren@Raven:~$ cat /dev/ttyUSB0 | sed -e '/^$/d' | ts
^C
wren@Raven:~$

但是,反转管道的顺序确实会产生输出,但当然不会删除不再是空白的行。其他替换工作正常,所以我知道到 sed 的管道正在工作。

wren@Raven:~$ cat /dev/ttyUSB0 |  ts | sed -e '/^$/d'
May 14 10:07:25 A_Sensor1,B_22.00,C_50.00
May 14 10:07:25
May 14 10:07:26 A_Sensor1,B_22.00,C_50.00
May 14 10:07:26
^C

所以我有点困惑。我大概可以让 sed 删除不需要的行,但在删除之前给它们加时间戳肯定是错误的方法。

我将非常感激您的解释和帮助。

答案1

直接回答这个问题,sed缓冲是唯一的问题。
你可以通过使用-u/--unbuffered标志告诉它不要缓冲来解决这个问题:

sed -u '/^$/d' /dev/ttyUSB0 | ts

使用测试工具(但您需要运行它来进行证明):

$ (echo -e 'banana\n\n'; sleep 2; echo 'cheese') | sed -u '/^$/d' | ts
May 14 11:26:05 banana
May 14 11:26:07 cheese

在使用其他流编辑器时,您可能会遇到类似的困境。它们似乎都希望缓冲一点。不过,它们都有解决方法。以下是我测试过的一系列命令:

... | mawk -W interactive '/./' | ts
... | gawk '/./ { print $0; fflush(); }' | ts
... | grep --line-buffered '.' | ts
... | perl -n -e 'print if /./' | ts

另一个想法是让它gawk处理它。它可以过滤非空行,并且打印日期为你(感谢 SO 的基隆):

awk '/./ { print strftime("%Y-%m-%d %H:%M:%S"), $0; fflush(); }' /dev/ttyUSB0

在线路进入后立即刷新。gawk如果你想这样做,这尤其有用其他事情...如果您想检查输出的第四列(前ts)是否与正则表达式匹配,您可以(例如$4~/\d{4}/)。Awk(及其变体)对于流处理非常灵活。

另一个测试工具:

$ gawk '/./ { print strftime("%Y-%m-%d %H:%M:%S"), $0; fflush(); }' <(
      echo -e 'banana\n\n';
      sleep 2;
      echo 'cheese'
  )
2014-05-14 11:13:59 banana
2014-05-14 11:14:01 cheese

答案2

while readbash 可以循环处理这个问题

(echo -e 'banana\n\n'; sleep 2; echo 'cheese') | 
while IFS= read -r line; do 
    [[ $line ]] && echo "$(date "+%F %T") line"
done
2014-05-14 06:34:06 banana
2014-05-14 06:34:08 cheese

您可以使用一个棘手的参数扩展来删除仅包含空格的行:删除所有前导空格并查看该行是否为空:

shopt -s extglob

(echo -e '  banana\n\t\n'; sleep 2; echo 'cheese') |
while IFS= read -r line; do
    [[ "${line/#+([[:blank:]])/}" ]] && echo "$(date "+%F %T") $line"
done

相关内容