場合によっては、出力をリダイレクトできないのはなぜですか?

場合によっては、出力をリダイレクトできないのはなぜですか?

例えば

# this line should output ssh banner
nc localhost 22
# this line should output the same string expect the replacements
nc localhost 22|sed 's/SSH/XXX/g'

nc localhost 22 > foo # after killing the process, I can see the banner in file `foo`
nc localhost 22|sed 's/SSH/XXX/g' > foo # however I see nothing when sed is used

sed前述のようにパイプnc出力を使用している場合は、sed出力をファイルにリダイレクトできません。

また、結果を渡すために他のコマンドも試しました(たとえばgreptr同じ結果)。

ただし、cat結果はファイルにパイプすることができます。奇妙な..

nc localhost 22|cat > foo

それでは、なぜこれが起こるのですか?catこの場合と他のコマンドの違いは何ですか?sed出力をファイルにどのようにリダイレクトできますかcat

答え1

$猫| sed -e 's/foo/bar/' > 出力.txt
金持ち
^C
$catoutput.txt
【何もありません】

しかし:

$猫| sed -e 's/foo/bar/' > 出力.txt
金持ち
^D
$catoutput.txt
バー

ファイルへの書き込み(ターミナルではない)は通常、Cライブラリ(stdio関数)によって数キロバイト単位でバッファリングされます。パイプを終了するためにCtrl-Cを押すと、sedパイプが終了する前にバッファを書き込む時間がないため、結果の出力ファイルは空になります。(これは終了シグナルを捕捉できないCライブラリのバグだと言えますが、実際にはそうです。)

Plainはcatおそらくラインのような複雑なものを処理しないので、stdioを使用しません。そのような部分は異なる場合cat -nもあれば異なる場合もあります。

パイプを終了するためにCtrl-Cを使用せずに、パイプ自体が完成するように配置できます。少なくとも、ncこの-qNオプションを使用して、次の後に終了するように指示します。窒素stdinが閉じた後の時間(秒単位)です。たとえば、次のようになります。

$ printf '' | nc -q0 localhost 22 | sed 's/SSH/FOO/' > output.txt 
$ cat output.txt 
FOO-2.0-OpenSSH_7.4p1 Debian-10+deb9u7

あるいは、バッファリングをオフにする方法は少なくともいくつかあります。 パイプラインでバッファリングをオフにするそして stdoutバッファを削除するには、「unbuffer」または「stdbuf」を使用しますか?

GNU sedに-u/--unbufferedスイッチもあります

入力ファイルから最小限のデータをロードし、出力バッファをより頻繁にフラッシュします。

関連情報