Я пытаюсь скопировать файлы через 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 байтов.
Почему небольшие файлы могут нормально переноситься, но к большим файлам добавляются байты?
-t
вариант, который нарушает передачу. Не используйте-t
или-T
, если они вам не нужны по очень конкретной причине. По умолчанию работает в подавляющем большинстве случаев, так что эти опции очень редко нужны.ssh -t cat
это единственный способ передачи файлов.Ответы:
TL; DR
Не используйте
-t
.-t
включает в себя псевдотерминал на удаленном хосте и должен использоваться только для запуска визуальных приложений из терминала.объяснение
Символ перевода строки (также называемый символом новой строки или
\n
) - это тот, который при отправке на терминал указывает терминалу переместить курсор вниз.Тем не менее, когда вы запускаете
seq 3
в терминале, именно здесьseq
пишет1\n2\n3\n
что-то вроде/dev/pts/0
, вы не видите:но
Почему это?
На самом деле, когда
seq 3
(илиssh host seq 3
в этом отношении) пишет1\n2\n3\n
, терминал видит1\r\n2\r\n3\r\n
. То есть переводы строки были переведены на возврат каретки (после чего терминалы перемещают курсор назад влево от экрана) и перевод строки.Это делается драйвером оконечного устройства. Точнее говоря, с помощью линейной дисциплины терминального (или псевдотерминального) устройства - программный модуль, который находится в ядре.
Вы можете контролировать поведение этой линии дисциплины с помощью
stty
команды. ПереводLF
->CRLF
включен с(который обычно включен по умолчанию). Вы можете отключить его с помощью:
Или вы можете отключить всю обработку вывода с помощью:
Если вы сделаете это и запустите
seq 3
, вы увидите:как и ожидалось.
Теперь, когда вы делаете:
seq
больше не пишет в терминал, он пишет в файл, перевод не выполняется. Такsome-file
и содержится1\n2\n3\n
. Перевод выполняется только при записи на терминальное устройство. И это только для отображения.аналогично, когда вы делаете:
ssh
пишет1\n2\n3\n
независимо от того, на чтоssh
идет вывод.На самом деле происходит то, что
seq 3
команда запускаетсяhost
со своим stdout, перенаправленным в канал.ssh
Сервер на хосте читает другой конец трубы и отправить его через зашифрованный канал для вашегоssh
клиента , иssh
клиент записывает его на своем стандартный вывод, в вашем случае псевдо-терминал, гдеLF
s переводится наCRLF
для отображения.Многие интерактивные приложения ведут себя по-разному, когда их стандартный вывод не является терминалом. Например, если вы запустите:
vi
ему не нравится, ему не нравится, когда его вывод идет в трубу. Он думает, что не разговаривает с устройством, которое может, например, понимать escape-последовательности позиционирования курсора.Так что
ssh
есть-t
возможность для этого. С этой опцией сервер ssh на хосте создает псевдо-терминальное устройство и делает его stdout (и stdin, и stderr) изvi
. То, чтоvi
пишет на этом оконечном устройстве, проходит через эту дисциплину линии удаленного псевдотерминала, читаетсяssh
сервером и отправляется по зашифрованному каналуssh
клиенту. Это то же самое , как и раньше , за исключением , что вместо того , чтобы использовать трубу , тоssh
сервер использует псевдо-терминал .Другое отличие состоит в том, что на стороне
ssh
клиента клиент устанавливает терминал вraw
режим. Это означает, что там не выполняется перевод (opost
отключен, а также другие действия на стороне ввода). Например, при вводе Ctrl-Cвместо прерыванияssh
этот^C
символ отправляется на удаленную сторону, где дисциплина линии удаленного псевдо-терминала отправляет прерывание на удаленную команду.Когда вы делаете:
seq 3
пишет1\n2\n3\n
в свой стандартный вывод, который является псевдо-терминальным устройством. Из - заonlcr
, что переводится на хозяина к1\r\n2\r\n3\r\n
и отправлен вам по зашифрованному каналу. На вашей стороне нет перевода (onlcr
отключен), поэтому1\r\n2\r\n3\r\n
отображается нетронутым (из-заraw
режима) и правильно на экране вашего эмулятора терминала.Теперь, если вы делаете:
Там нет никакой разницы сверху.
ssh
напишу тоже самое1\r\n2\r\n3\r\n
, но на этот раз вsome-file
.Таким образом, в основном все
LF
в результатеseq
были переведеныCRLF
вsome-file
.То же самое, если вы делаете:
Все
LF
символы (0x0a байтов) переводятся в CRLF (0x0d 0x0a).Это, вероятно, причина коррупции в вашем файле. В случае второго меньшего файла, так получилось, что файл не содержит байтов 0x0a, поэтому искажения отсутствуют.
Обратите внимание, что вы можете получить различные типы повреждений с разными настройками tty. Другой потенциальный тип повреждения, связанный с тем
-t
, что ваши загрузочные файлыhost
(~/.bashrc
,~/.ssh/rc
...) записывают вещи в их stderr, потому что с-t
stdout и stderr удаленной оболочки в конечном итоге объединяются вssh
stdout (они оба переходят к псевдо -терминальное устройство).Вы не хотите, чтобы пульт дистанционного управления
cat
выводил на терминальное устройство.Вы хотите:
Вы могли бы сделать:
Это будет работать (за исключением случая записи в stderr, рассмотренного выше), но даже это будет неоптимальным, так как у вас будет работать этот ненужный псевдотерминальный уровень
host
.Еще немного веселья:
ХОРОШО.
LF
переведено наCRLF
Хорошо снова
Это еще одна форма пост-обработки вывода, которая может быть выполнена дисциплиной терминальной линии.
ssh
отказывается указывать серверу использовать псевдо-терминал, когда его собственный ввод не является терминалом. Вы можете заставить это,-tt
хотя:Дисциплина линии делает намного больше на стороне ввода.
Здесь,
echo
не читает его ввод и не было предложено вывести это,x\r\n\n
так откуда это взялось? Это локальныйecho
псевдотерминал (stty echo
).ssh
Сервер кормленияx\n
она считывается из клиента к главной стороне удаленного псевдо-терминала. И дисциплина линии этого повторяет это (прежде, чемstty opost
бежать, именно поэтому мы видим,CRLF
а неLF
). Это не зависит от того, читает ли удаленное приложение что-либо из стандартного ввода или нет.0x3
Символ эхо , как^C
(^
аC
) из - за ,stty echoctl
а оболочка и сон получают SIGINT , потому чтоstty isig
.Так что пока:
достаточно плохо, но
передавать файлы в другую сторону намного хуже. Вы получите некоторые CR -> LF перевода, но и проблемы со всеми специальными символами (
^C
,^Z
,^D
,^?
,^S
...) , а также пультcat
не будет видеть ВФ , когда конецlocal-file
достигается только тогда , когда^D
отправляется после\r
,\n
или другой,^D
как при выполненииcat > file
в вашем терминале.источник
При использовании этого метода для копирования файла файлы выглядят иначе.
Удаленный сервер
Локальный сервер
Выполнение вашей
ssh ... cat
команды:Результаты в этом файле на локальном сервере:
Расследовать почему?
Исследование полученного файла на локальной стороне показывает, что он был поврежден. Если вы
-t
отключитеssh
команду, то она будет работать как положено.Контрольные суммы теперь тоже работают:
источник