TL; DR
使用しないでください-t。-tリモートホスト上の擬似端末を使用するため、端末からビジュアルアプリケーションを実行する場合にのみ使用する必要があります。
説明
改行文字(改行またはとも呼ばれます\n)は、端末に送信されると、カーソルを下に移動するよう端末に指示する文字です。
しかし、seq 3ターミナルで実行すると、のような場所にseq書き込みが行われますが、表示されません:1\n2\n3\n/dev/pts/0
1
2
3
しかし
1
2
3
何故ですか?
実際、seq 3(またはssh host seq 3そのことに関して)書き込む1\n2\n3\nとき、端末はを参照し1\r\n2\r\n3\r\nます。つまり、改行はキャリッジリターン(カーソルを画面の左に戻す)と改行に変換されています。
これは、端末デバイスドライバーによって行われます。より正確には、端末(または擬似端末)デバイスの回線規律によって、カーネルに常駐するソフトウェアモジュール。
sttyコマンドを使用して、その回線制御の動作を制御できます。翻訳はLF- > CRLFをオンにします
stty onlcr
(通常、デフォルトで有効になっています)。次の方法でオフにできます。
stty -onlcr
または、次の方法ですべての出力処理をオフにすることができます。
stty -opost
それを実行して実行するとseq 3、以下が表示されます。
$ stty -onlcr; seq 3
1
2
3
予想通り。
今、あなたがするとき:
seq 3 > some-file
seq端末への書き込みではなく、ファイルへの書き込みです。変換は行われていません。だから、some-file含まれていません1\n2\n3\n。変換は、端末デバイスへの書き込み時にのみ行われます。そして、それは表示のためだけに行われます。
同様に、あなたがするとき:
ssh host seq 3
sshが1\n2\n3\n何にssh出力されるかに関係なく書き込みを行っています。
実際に起こることは、seq 3コマンドがhostパイプにリダイレクトされたstdoutで実行されることです。sshホスト上のサーバーは、パイプのもう一方の端を読み取り、自分のに暗号化されたチャネルを介してそれを送信するsshクライアントとsshクライアントがあなたのケースでは、その標準出力に疑似端末デバイス、それを書き込み、LFsはに翻訳されてCRLF表示するために。
多くの対話型アプリケーションは、stdoutが端末でない場合に異なる動作をします。たとえば、次を実行すると:
ssh host vi
viそれが気に入らない、パイプに出力されるのが気に入らない。たとえば、カーソルポジショニングエスケープシーケンスを理解できるデバイスとは通信していないと考えています。
そのsshための-tオプションもあります。このオプションを使用すると、ホスト上のsshサーバーは擬似端末デバイスを作成し、その標準出力(および標準入力、標準エラー出力)を作成しviます。何viその端末デバイスに書き込みすると、そのリモート仮想端末回線の規律を経て、によって読み取られssh、サーバーとの暗号化チャネルを介して送信されるsshクライアント。パイプを使用する代わりに、sshサーバーがpseudo-terminalを使用することを除いて、以前と同じです。
もう1つの違いは、クライアント側では、sshクライアントが端末をrawモードに設定することです。これは、そこで翻訳が行われないことを意味しopostます(無効化され、他の入力側の動作も行われます)。たとえばCtrl-C、割り込みsshではなくを入力すると、その^C文字はリモート側に送信され、リモート擬似端末の回線制御が割り込みをリモートコマンドに送信します。
行うとき:
ssh -t host seq 3
seq 31\n2\n3\n擬似端末デバイスであるstdoutに書き込みます。以下のためonlcrに翻訳されることを、ホスト上に1\r\n2\r\n3\r\n、暗号化チャネルを介してあなたに送信。あなたの側には翻訳がありません(onlcr無効になっている)ので、1\r\n2\r\n3\r\nそのままで(rawモードのため)そのまま表示され、ターミナルエミュレータの画面に正しく表示されます。
今、あなたがするなら:
ssh -t host seq 3 > some-file
上記と違いはありません。sshは同じことを書きます:1\r\n2\r\n3\r\nが、今回はにsome-file。
したがって、基本的LFにの出力のすべてはにseq変換さCRLFれていsome-fileます。
行う場合も同じです:
ssh -t host cat remote-file > local-file
すべてのLF文字(0x0aバイト)はCRLF(0x0d 0x0a)に変換されています。
これがおそらくファイルの破損の原因です。2番目に小さいファイルの場合は、ファイルに0x0aバイトが含まれていないため、破損はありません。
tty設定が異なると、さまざまな種類の破損が発生する可能性があることに注意してください。別の潜在的な破損のタイプは-t、host(~/.bashrc、~/.ssh/rc...)のスタートアップファイルがstderrに書き込みを行う場合です。これ-tは、リモートシェルのstdoutとstderrが最終的にsshstdout にマージされるためです(両方ともpseudo -端末デバイス)。
そこにあるリモートcatから端末デバイスへの出力は望ましくありません。
あなたが欲しい:
ssh host cat remote-file > local-file
できること:
ssh -t host 'stty -opost; cat remote-file` > local-file
それは動作します(上記で説明したstderr破損ケースへの書き込みを除く)が、それでも、不要な擬似端末層が実行されているので、最適ではありませんhost。
もう少し楽しい:
$ ssh localhost echo | od -tx1
0000000 0a
0000001
OK。
$ ssh -t localhost echo | od -tx1
0000000 0d 0a
0000002
LF に翻訳 CRLF
$ ssh -t localhost 'stty -opost; echo' | od -tx1
0000000 0a
0000001
OK
$ ssh -t localhost 'stty olcuc; echo x'
X
これは、端末回線制御によって実行できる出力後処理の別の形式です。
$ echo x | ssh -t localhost 'stty -opost; echo' | od -tx1
Pseudo-terminal will not be allocated because stdin is not a terminal.
stty: standard input: Inappropriate ioctl for device
0000000 0a
0000001
ssh自身の入力が端末でない場合に、擬似端末を使用するようサーバーに指示することを拒否します。あなたはそれでも強制することができ-ttます:
$ echo x | ssh -tt localhost 'stty -opost; echo' | od -tx1
0000000 x \r \n \n
0000004
回線制御は入力側でより多くのことを行います。
ここでechoは、入力を読み取らず、出力するx\r\n\nように求められなかったので、どこから来たのですか?これechoは、リモートの擬似端末(stty echo)のローカルです。sshサーバが供給されx\n、それが遠隔疑似端末のマスタ側にクライアントから読み取ら。そして、そのラインの規律はそれをエコーバックします(前stty opostに実行されるCRLFので、aが表示され、not は表示されませんLF)。これは、リモートアプリケーションがstdinから何かを読み取るかどうかとは無関係です。
$ (sleep 1; printf '\03') | ssh -tt localhost 'trap "echo ouch" INT; sleep 2'
^Couch
0x3文字は、次のようにエコーバックされる^C(^とC)のためstty echoctlとシェルと睡眠があるためSIGINTを受け取りますstty isig。
それで:
ssh -t host cat remote-file > local-file
十分に悪いですが、
ssh -tt host 'cat > remote-file' < local-file
他の方法でファイルを転送するのは非常に悪いことです。> LF翻訳するだけでなく、すべての特殊文字での問題( -あなたは、いくつかのCRを得るでしょう^C、^Z、^D、^?、^Sも...)とは、リモートでcatの最後の時にEOFは表示されませんlocal-file達している場合にのみ、^D後に送信され\r、\nまたは、端末で^D行うときなどcat > file。
-t転送を中断するのはオプションです。非常に特定の理由で必要でない限り、-tまたはを使用しないでください-T。デフォルトはほとんどの場合に機能するため、これらのオプションはほとんど必要ありません。