Bash:如何实时过滤SSH命令中的tee输出?

4
我的目标是记录我的ssh会话中使用的命令的时间戳日志。
只要输出没有被过滤,Tee就可以实时工作。当我使用tail -F test.log时,以下命令将实时追加输出:
#!/bin/bash
ssh "$@" | tee -a test.log

然而,当我尝试按照这个问题中提出的方法修改tee的输出时,输出就不再是实时的了。例如:

#!/bin/bash
ssh "$@" | tee >(grep -e '.*\@.*\:.*\$.*' --color=never >> test.log)

奇怪的是,如果我用"yes"命令代替ssh命令,输出就能够实时过滤。

实时处理非常重要,因为我的脚本需要在每一行末尾添加当前时间戳并尽可能地删除输出。以下是我的脚本:

#!/bin/bash
logfile=~/test.log
desc="sshlog ${@}"
tab="\t"
format_line() {
    while IFS= read -r line; do
        echo "$(date +"%Y-%m-%d %H:%M:%S %z")${tab}${desc}${tab}${line}"
    done
}
echo "[START]" | format_line >> $logfile
ssh "$@" | tee >(grep -e '.*\@.*\:.*\$.*' --color=never | format_line >> $logfile)
echo "[END]" | format_line >> $logfile

我应该如何解决这个问题,为什么ssh命令在使用tee时与yes命令表现不同?
1个回答

11
问题很可能是grep正在缓冲其输出--选择大块的输入,过滤它们,并输出结果--因此它可以顺畅地处理yes的输出(因为yes快速生成了大量的输入供其过滤和输出),而您的ssh命令可能不能以同样的速度产生那么多的输出。
许多版本的grep提供了一种调整这个缓冲的机制。由于您在Linux上,您可能正在使用GNU Grep,它提供了一个--line-buffered标志来实现这个目的(请参见GNU Grep手册的“其他选项”部分),以便输出只缓冲一行:
ssh "$@" | tee >(grep -e '.*\@.*\:.*\$.*' --color=never --line-buffered >> test.log)

如果您使用awk而不是grep,您将使用fflush();。这个问题与此相关:https://dev59.com/rWEi5IYBdhLWcg3wpdfJ - phyatt
@ruakh 我尝试了你的tee输出重定向命令一般来说:'某些命令' | tee >( '某些过滤' > '输出文件' ) 在/bin/bash中可以工作。 但是我在/bin/sh中使用它时遇到语法错误, 例如:语法错误:“(”意外。有什么建议吗? 感谢您的支持 :) - GrayFox
@GrayFox:这里的进程替换(>(...)功能)实际上是在问题中--我只是在这里包含它以展示上下文中的--line-buffered选项--所以我认为这个问题问如何在不支持它的shell中近似进程替换是离题的。幸运的是,你不需要问:这个问题已经链接到https://dev59.com/e2fWa4cB1Zd3GeqPleWB,其中有几个回答显示了*非*基于进程替换的方法。 - ruakh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接