Почему этот двоичный файл, передаваемый через «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, если они вам не нужны по очень конкретной причине. По умолчанию работает в подавляющем большинстве случаев, так что эти опции очень редко нужны.
Касперд

3
Никогда не думал, что скажу это в этом столетии, но вы можете попробовать uuencode и uudecode, если ssh -t catэто единственный способ передачи файлов.
Марк Плотник

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

sshпишет 1\n2\n3\nнезависимо от того, на что sshидет вывод.

На самом деле происходит то, что seq 3команда запускается hostсо своим stdout, перенаправленным в канал. sshСервер на хосте читает другой конец трубы и отправить его через зашифрованный канал для вашего sshклиента , и sshклиент записывает его на своем стандартный вывод, в вашем случае псевдо-терминал, где LFs переводится на CRLFдля отображения.

Многие интерактивные приложения ведут себя по-разному, когда их стандартный вывод не является терминалом. Например, если вы запустите:

ssh host vi

viему не нравится, ему не нравится, когда его вывод идет в трубу. Он думает, что не разговаривает с устройством, которое может, например, понимать escape-последовательности позиционирования курсора.

Так что sshесть -tвозможность для этого. С этой опцией сервер ssh на хосте создает псевдо-терминальное устройство и делает его stdout (и stdin, и stderr) из vi. То, что viпишет на этом оконечном устройстве, проходит через эту дисциплину линии удаленного псевдотерминала, читается sshсервером и отправляется по зашифрованному каналу sshклиенту. Это то же самое , как и раньше , за исключением , что вместо того , чтобы использовать трубу , то sshсервер использует псевдо-терминал .

Другое отличие состоит в том, что на стороне sshклиента клиент устанавливает терминал в rawрежим. Это означает, что там не выполняется перевод ( opostотключен, а также другие действия на стороне ввода). Например, при вводе Ctrl-Cвместо прерывания sshэтот ^Cсимвол отправляется на удаленную сторону, где дисциплина линии удаленного псевдо-терминала отправляет прерывание на удаленную команду.

Когда вы делаете:

ssh -t host seq 3

seq 3пишет 1\n2\n3\nв свой стандартный вывод, который является псевдо-терминальным устройством. Из - за 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).

Это, вероятно, причина коррупции в вашем файле. В случае второго меньшего файла, так получилось, что файл не содержит байтов 0x0a, поэтому искажения отсутствуют.

Обратите внимание, что вы можете получить различные типы повреждений с разными настройками tty. Другой потенциальный тип повреждения, связанный с тем -t, что ваши загрузочные файлы host( ~/.bashrc, ~/.ssh/rc...) записывают вещи в их stderr, потому что с -tstdout и stderr удаленной оболочки в конечном итоге объединяются в sshstdout (они оба переходят к псевдо -терминальное устройство).

Вы не хотите, чтобы пульт дистанционного управления 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

ХОРОШО.

$ ssh -t localhost echo | od -tx1
0000000 0d 0a
0000002

LF переведено на CRLF

$ ssh -t localhost 'stty -opost; echo' | od -tx1
0000000 0a
0000001

Хорошо снова

$ 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а не LF). Это не зависит от того, читает ли удаленное приложение что-либо из стандартного ввода или нет.

$ (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

передавать файлы в другую сторону намного хуже. Вы получите некоторые CR -> LF перевода, но и проблемы со всеми специальными символами ( ^C, ^Z, ^D, ^?, ^S...) , а также пульт catне будет видеть ВФ , когда конец 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 - не беспокойтесь, вы соглашаетесь с тем, что, как вы считаете, именно те, которые больше всего помогают вам в качестве ОП, 8-). Его способность объяснить, почему все происходит, не имеет себе равных, за исключением Жиля.
SLM
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.