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-сервер на хості створює псевдо-термінальний пристрій і робить 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в свій 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).
Це, мабуть, причина корупції у вашому файлі. У випадку з другим меншим файлом так виходить, що файл не містить 0x0a байт, тому немає пошкодження.
Зауважте, що ви можете отримати різні типи корупції з різними налаштуваннями. Ще один потенційний тип корупції, пов’язаний із -tтим, що якщо ваші файли запуску на host( ~/.bashrc, ~/.ssh/rc...) записують речі в їх stderr, тому що, -tколи stdout і stderr віддаленої оболонки в кінцевому підсумку об'єднуються в sshstdout s (вони обидва переходять до псевдо -термінальний пристрій).
Ви не хочете, щоб пульт catвиводив на термінальний пристрій там.
Ти хочеш:
ssh host cat remote-file > local-file
Ви можете зробити:
ssh -t host 'stty -opost; cat remote-file` > local-file
Це може спрацювати (за винятком написання вище обговореної вище корупційної справи), але навіть це було б неоптимальним, оскільки у вас буде працювати цей непотрібний псевдотермінальний шар 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). Це незалежно від того, чи читає віддалений додаток щось із 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
переносити файли іншим способом набагато гірше. Ви отримаєте деякі CR -> LF перекладу, але і проблеми з усіма спеціальними символами ( ^C, ^Z, ^D, ^?, ^S...) , а також пульт catне бачитиме ВФ , коли кінець local-fileдосягається тільки тоді , коли ^Dвідправляється після \r, \nабо інший, ^Dяк це робиться cat > fileу вашому терміналі.
-tваріант, який порушує передачу. Не використовуйте-tабо-T, якщо вони вам не знадобляться з дуже конкретної причини. За замовчуванням діє переважна більшість випадків, тому такі варіанти дуже рідкісні.