「ssh -t」を介して転送されるこのバイナリファイルが変更されるのはなぜですか?


29

SSH経由でファイルコピーしようとしscpていますが、必要な正確なファイル名がわからないため使用できません。小さなバイナリファイルとテキストファイルは正常に転送されますが、大きなバイナリファイルは変更されます。サーバー上のファイルは次のとおりです。

remote$ ls -la
-rw-rw-r--  1 user user 244970907 Aug 24 11:11 foo.gz
remote$ md5sum foo.gz 
9b5a44dad9d129bab52cbc6d806e7fda foo.gz

移動した後のファイルは次のとおりです。

local$ time ssh me@server.com -t 'cat /path/to/foo.gz' > latest.gz

real    1m52.098s
user    0m2.608s
sys     0m4.370s
local$ md5sum latest.gz
76fae9d6a4711bad1560092b539d034b  latest.gz

local$ ls -la
-rw-rw-r--  1 dotancohen dotancohen 245849912 Aug 24 18:26 latest.gz

ダウンロードしたファイルは、サーバー上のファイルより大きいことに注意してください!ただし、非常に小さなファイルで同じことを行うと、すべてが期待どおりに機能します:

remote$ echo "Hello" | gzip -c > hello.txt.gz
remote$ md5sum hello.txt.gz
08bf5080733d46a47d339520176b9211  hello.txt.gz

local$ time ssh me@server.com -t 'cat /path/to/hello.txt.gz' > hi.txt.gz

実0m3.041sユーザー0m0.013s sys 0m0.005s

local$ md5sum hi.txt.gz
08bf5080733d46a47d339520176b9211  hi.txt.gz

この場合、両方のファイルサイズは26バイトです。

なぜ小さなファイルは問題なく転送されるのに、大きなファイルにはいくつかのバイトが追加されるのですか?


10
-t転送を中断するのはオプションです。非常に特定の理由で必要でない限り、-tまたはを使用しないでください-T。デフォルトはほとんどの場合に機能するため、これらのオプションはほとんど必要ありません。
カスペルド14

3
今世紀中にこれを言うとは思わssh -t catなかったが、ファイルを転送する唯一の方法である場合は、uuencodeとuudecodeを試してみてください。
マークPlotnick 14

1
@MarkPlotnickの最新バージョンのuuencode / uudecodeの名前がbase64 / base64 -dになりました
-Archemar

回答:


60

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

ssh1\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設定が異なると、さまざまな種類の破損が発生する可能性があることに注意してください。別の潜在的な破損のタイプは-thost~/.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


5

その方法を使用してファイルをコピーすると、ファイルが異なるように見えます。

リモートサーバー

ls -l | grep vim_cfg
-rw-rw-r--.  1 slm slm 9783257 Aug  5 16:51 vim_cfg.tgz

ローカルサーバー

ssh ... catコマンドの実行:

$ ssh dufresne -t 'cat ~/vim_cfg.tgz' > vim_cfg.tgz

ローカルサーバー上のこのファイルの結果:

$ ls -l | grep vim_cfg.tgz 
-rw-rw-r--. 1 saml saml 9820481 Aug 24 12:13 vim_cfg.tgz

理由を調査していますか?

ローカル側で結果のファイルを調査すると、ファイルが破損していることがわかります。あなたが取る場合は-t、あなたのスイッチをsshコマンド予想通り、それは動作します。

$ ssh dufresne 'cat ~/vim_cfg.tgz' > vim_cfg.tgz

$ ls -l | grep vim_cfg.tgz
-rw-rw-r--. 1 saml saml 9783257 Aug 24 12:17 vim_cfg.tgz

チェックサムも機能するようになりました:

# remote server
$ ssh dufresne "md5sum ~/vim_cfg.tgz"
9e70b036836dfdf2871e76b3636a72c6  /home/slm/vim_cfg.tgz

# local server
$ md5sum vim_cfg.tgz 
9e70b036836dfdf2871e76b3636a72c6  vim_cfg.tgz

シム、ありがとう。実際、あなたが最初に正しい答えを投稿したのですが、彼の説明の深さから、私は選ばれた答えにステファンを選択しました。心配しないでください、あなたは私が学んでいる長い投稿履歴を持っています、そしてもちろん私は私が学んだそれらの投稿を支持します。ありがとうございました。
dotancohen

@dotancohen-心配する必要はありません。OPとして最も役立つのはあなたがAであると感じるものです。何が起こるのかを説明する彼の能力は、Gillesを除いて比類のないものです。
slm
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.