将命令输出分成新行而不是刷新同一行

将命令输出分成新行而不是刷新同一行

我正在尝试编写一个涉及以下内容的脚本快照RAID,其中 的进度输出snapraid sync带有时间戳并记录到文件和标准输出中。我面临的问题是将snapraid sync其进度输出到每秒刷新的一行上。这意味着当在脚本中运行它时,剩下的唯一输出是类似100% completed, 7737 MB accessed in 0:00.您无法回顾命令是如何运行的。

通过使用tssedtee,我可以部分实现我的目标:

#!/bin/bash

LOG_FILE="/tmp/snapRAID.log"

exec 1>> >(ts '[%Y-%m-%d %H:%M:%S]') 2>&1

snapraid sync | sed -e 's/\r/\n/g' | tee >(ts "[%Y-%m-%d %H:%M:%S]" >> $LOG_FILE)

stdout这将导致和产生以下输出/tmp/snapRAID.log(以及 的其余输出snapraid sync):

[2023-04-23 16:24:51] 0%, 0 MB
[2023-04-23 16:24:51] 5%, 552 MB
[2023-04-23 16:24:51] 12%, 1332 MB
[2023-04-23 16:24:51] 17%, 1942 MB
[2023-04-23 16:24:51] 21%, 2311 MB
[2023-04-23 16:24:51] 27%, 3044 MB, 622 MB/s, 1188 stripe/s, CPU 14%, 0:00 ETA
[2023-04-23 16:24:51] 35%, 3836 MB, 656 MB/s, 1253 stripe/s, CPU 14%, 0:00 ETA
[2023-04-23 16:24:51] 42%, 4612 MB, 676 MB/s, 1306 stripe/s, CPU 15%, 0:00 ETA
[2023-04-23 16:24:51] 49%, 5005 MB, 636 MB/s, 1334 stripe/s, CPU 15%, 0:00 ETA
[2023-04-23 16:24:51] 58%, 5493 MB, 617 MB/s, 1400 stripe/s, CPU 16%, 0:00 ETA
[2023-04-23 16:24:51] 67%, 5987 MB, 581 MB/s, 1449 stripe/s, CPU 16%, 0:00 ETA
[2023-04-23 16:24:51] 75%, 6415 MB, 564 MB/s, 1470 stripe/s, CPU 16%, 0:00 ETA
[2023-04-23 16:24:51] 80%, 6694 MB, 536 MB/s, 1429 stripe/s, CPU 15%, 0:00 ETA
[2023-04-23 16:24:51] 84%, 6916 MB, 507 MB/s, 1376 stripe/s, CPU 15%, 0:00 ETA
[2023-04-23 16:24:51] 92%, 7314 MB, 498 MB/s, 1388 stripe/s, CPU 15%, 0:00 ETA
[2023-04-23 16:24:51] 97%, 7574 MB, 433 MB/s, 1268 stripe/s, CPU 13%, 0:00 ETA

问题是这个输出不是实时的 -snapraid sync只有在运行完成后,整个输出才会出现在标准输出和日志文件中。如果我删除该tee元素并将其输出到stdout.如果我想了解snapraid sync运行中的进展情况,这对我没有帮助。该测试实例在几秒钟内完成同步,但我想要运行它的机器可能需要几个小时,并且我希望能够在运行过程中检查命令的进度,同时还能够回顾运行之前发生的事情并查看时间戳。

我尝试过以下一些想法,但没有成功:

unbuffer snapraid sync | sed -e 's/\r/\n/g' | tee >(ts "[%Y-%m-%d %H:%M:%S]" >> $LOG_FILE)

snapraid sync | sed -u -e 's/\r/\n/g' | tee >(ts "[%Y-%m-%d %H:%M:%S]" >> $LOG_FILE)

有谁知道我如何才能使这个带有时间戳的换行输出实时发生?

提前谢谢了!

答案1

sed缓冲其输出,因为它不会发送到 tty。某些sed实现支持-u禁用该缓冲的选项,或者您可以使用stdbuf, withsed或 with tr,这会更合适,特别是\r考虑\nsed.

snapraid sync |
  stdbuf -oL tr '\r' '\n' |
  ts '[%Y-%m-%d %H:%M:%S]' |
  tee -a "$LOG_FILE"

如果snapraid在不进入终端时也缓冲其输出,您可以stdbuf在那里尝试相同的技巧,尽管您可能希望在那里stdbuf -o0(无缓冲)而不是stdbuf -oL(基于行的缓冲):

stdbuf -o0 snapraid sync |
  stdbuf -oL tr '\r' '\n' |
  ts '[%Y-%m-%d %H:%M:%S]' |
  tee -a "$LOG_FILE"

无论如何,stdbuf这也不是标准命令,但在可用的情况下,应该比使用伪 tty 尝试说服应用unbuffer程序expect不要缓冲但已知会破坏输出的脚本更可取。

-u都不是标准选项sed

请注意,许多sed实现还会提前读取一行,以便能够知道地址何时$匹配,这意味着即使它到达终端设备,输出也会有一行延迟。

ts是一个明确禁用缓冲的perl脚本。$| = 1在我的测试中,tee即使目标文件和标准输出都不是终端设备,GNU 至少不会缓冲其输出。

顺便说一句,从安全角度来看,在世界可写目录中写入具有固定名称的文件是/tmp不好的做法,应该避免。要么使用它mktemp来保证您在那里获得自己的临时文件,要么将该文件写入您的目录,例如其他人都无法在其中植入恶意符号链接。

相关内容