首页 > 解决方案 > 没有 -tt 的 stderr 的 ssh 缓冲问题

问题描述

我正在尝试使用输出到标准错误和标准输出的 ssh 远程运行 bash 脚本。
当我在终端中正常运行时,我会按顺序获得输出。但是当使用 ssh 运行时,我不会,例如:

$ ssh -T user@host bash <<< "echo hi >&2 && echo bye"
bye
hi

标准错误延迟到标准输出之后。这与我在网上看到的关于缓冲 stderr 和 stdout 之间的差异的大多数事情形成对比,这些差异表明 stderr 是无缓冲的(因此将首先输出)。

但无论如何,我尝试的几乎所有东西都无法让线条按顺序输出。我已经尝试将 stdbuf -e0 -o0 -i0 拍打到命令的几乎每个部分(在 ssh 之前,在 bash 之前,在 echo 之前)。我尝试了不同的方式来传递命令(有bash -cbash根本没有)。

我发现,如果我将 -tt 传递给 ssh,我发现它似乎始终如一地工作的一种方法。但是使用 -tt 似乎会添加自己的蠕虫罐头,如果可能的话,我不希望这样做。

还有其他方法可以使 ssh 输出缓冲更“正常”吗?

标签: bashsshstdoutstderrflush

解决方案


stderr 无缓冲仅意味着数据立即写入 fd,但它不能保证写入的数据何时由接收器(pty、socket、管道……)处理。

对于ssh -T,请参阅此示例(在 Debian Linux 上):

[STEP 103] # ssh -T 127.0.0.1 'ls -l /proc/$$/fd'
total 0
lr-x------ 1 root root 64 Aug  4 13:59 0 -> pipe:[4610144]
l-wx------ 1 root root 64 Aug  4 13:59 1 -> pipe:[4610145]
l-wx------ 1 root root 64 Aug  4 13:59 2 -> pipe:[4610146]
lr-x------ 1 root root 64 Aug  4 13:59 3 -> /proc/23662/fd
[STEP 104] #

如图所示,sshd使用 3 个管道与其子进程通信(这种方式sshd能够区分 stdout 数据和 stderr 数据)。对于孩子来说,保证写入 stderr 的数据将立即发送到管道,但完全由sshd进程决定何时处理数据。(更新:即使在sshd将数据发送回ssh客户端之后,仍然由ssh客户端决定何时处理接收到的数据。)

至于ssh -tt,将为孩子分配一个pty。子进程的stdin、stdout和stderr都会在pty上打开,sshd无法区分stdour数据和stderr数据,所以结果是预期的。


要验证ssh -tt无法区分 stdout 数据和 stderr 数据:

[STEP 109] # ssh -T 127.0.0.1 'echo hi; echo bye >&2'
hi
bye
[STEP 110] # ssh -T 127.0.0.1 'echo hi; echo bye >&2' 2> /dev/null
hi
[STEP 111] #
[STEP 112] # ssh -tt 127.0.0.1 'echo hi; echo bye >&2'
hi
bye
Connection to 127.0.0.1 closed.
[STEP 113] # ssh -tt 127.0.0.1 'echo hi; echo bye >&2' 2> /dev/null
hi
bye
[STEP 114] #

推荐阅读