TL; DR
Nie używać -t. -tobejmuje pseudo-terminal na zdalnym hoście i powinien być używany tylko do uruchamiania aplikacji wizualnych z terminala.
Wyjaśnienie
Znak przesunięcia linii (znany również jako znak nowej linii lub \n) to ten, który po wysłaniu do terminala informuje terminal o przesunięciu kursora w dół.
Jednak podczas uruchamiania seq 3w terminalu, czyli tam, gdzie seqpisze 1\n2\n3\ncoś takiego /dev/pts/0, nie widzisz:
1
2
3
ale
1
2
3
Dlaczego?
W rzeczywistości, kiedy seq 3(lub ssh host seq 3w tym przypadku) pisze 1\n2\n3\n, terminal widzi 1\r\n2\r\n3\r\n. Oznacza to, że przesunięcia linii zostały przetłumaczone na powrót karetki (po której terminale przesuwają kursor z powrotem na lewą stronę ekranu) i przesunięcie linii.
Odbywa się to przez sterownik urządzenia końcowego. Dokładniej, według dyscypliny liniowej urządzenia terminalowego (lub pseudo-terminalnego), moduł oprogramowania, który znajduje się w jądrze.
Za pomocą polecenia można kontrolować zachowanie tej dyscypliny liniowej stty. Tłumaczenie LF-> CRLFjest włączone za pomocą
stty onlcr
(który zazwyczaj jest domyślnie włączony). Możesz to wyłączyć za pomocą:
stty -onlcr
Lub możesz wyłączyć wszystkie przetwarzanie danych wyjściowych za pomocą:
stty -opost
Jeśli to zrobisz i uruchomisz seq 3, zobaczysz:
$ stty -onlcr; seq 3
1
2
3
zgodnie z oczekiwaniami.
Teraz, kiedy to zrobisz:
seq 3 > some-file
seqnie zapisuje już na terminalu, zapisuje do pliku, tłumaczenie nie jest wykonywane. Więc some-filezawiera 1\n2\n3\n. Tłumaczenie jest wykonywane tylko podczas pisania na urządzeniu końcowym. I to tylko na pokaz.
podobnie, gdy robisz:
ssh host seq 3
sshpisze 1\n2\n3\nniezależnie od tego, do czego sshzmierza wyjście.
W rzeczywistości dzieje się tak, że seq 3polecenie jest uruchamiane hostz przekierowaniem standardu na potok. sshSerwera hosta odczytuje na drugi koniec rury i wysłać go na zaszyfrowany kanał do sshklienta, a sshklient zapisuje go na jego standardowe wyjście, w przypadku urządzenia pseudo-terminali, gdzie LFs są tłumaczone na CRLFna wyświetlaczu.
Wiele interaktywnych aplikacji zachowuje się inaczej, gdy ich standardowe wyjście nie jest terminalem. Na przykład, jeśli uruchomisz:
ssh host vi
vinie lubi tego, nie lubi, aby jego wyjście trafiało do potoku. Uważa, że nie rozmawia z urządzeniem, które może na przykład zrozumieć sekwencje specjalne pozycjonowania kursora.
Tak też sshjest -topcja. Dzięki tej opcji serwer ssh na hoście tworzy pseudoterminalowe urządzenie i sprawia, że stdout (i stdin i stderr) z vi. To, co vipisze na tym urządzeniu końcowym, przechodzi przez tę dyscyplinę zdalnej linii pseudoterminalowej i jest odczytywane przez sshserwer i wysyłane zaszyfrowanym kanałem do sshklienta. To samo, jak poprzednio z tym, że zamiast stosowania rury The sshserwera wykorzystuje pseudoterminal .
Inna różnica polega na tym, że po stronie sshklienta klient ustawia terminal w rawtrybie. Oznacza to, że nie wykonuje się tam tłumaczenia ( opostjest wyłączone, a także inne zachowania po stronie wejściowej). Na przykład, gdy piszesz Ctrl-C, zamiast przerywania ssh, ^Cznak ten jest wysyłany do strony zdalnej, gdzie dyscyplina liniowa zdalnego pseudo-terminala wysyła przerwanie do polecenia zdalnego.
Kiedy to zrobisz:
ssh -t host seq 3
seq 3zapisuje 1\n2\n3\nna standardowe wyjście, które jest pseudoterminalnym urządzeniem. Ze względu onlcr, że zostanie przetłumaczony na hosta do 1\r\n2\r\n3\r\ni wysłane przez szyfrowany kanał. Po twojej stronie nie ma tłumaczenia ( onlcrwyłączone), więc 1\r\n2\r\n3\r\njest wyświetlane nietknięte (z powodu rawtrybu) i poprawnie na ekranie emulatora terminala.
Teraz, jeśli to zrobisz:
ssh -t host seq 3 > some-file
Nie ma różnicy z góry. sshnapisze to samo: 1\r\n2\r\n3\r\nale tym razem w some-file.
Więc w zasadzie wszystkie dane LFwyjściowe seqzostały przetłumaczone CRLFna some-file.
To samo, jeśli wykonasz:
ssh -t host cat remote-file > local-file
Wszystkie LFznaki (0x0a bajtów) są tłumaczone na CRLF (0x0d 0x0a).
Prawdopodobnie jest to przyczyną uszkodzenia pliku. W przypadku drugiego mniejszego pliku dzieje się tak, że plik nie zawiera bajtów 0x0a, więc nie ma uszkodzenia.
Pamiętaj, że możesz uzyskać różne rodzaje uszkodzenia przy różnych ustawieniach tty. Innym potencjalnym rodzajem uszkodzenia związanego z tym -tjest to, że pliki startowe na host( ~/.bashrc, ~/.ssh/rc...) zapisują rzeczy do swojego stderr, ponieważ wraz -tze stdout i stderr zdalnej powłoki są scalane w sshstdout (oba idą do pseudo - urządzenie końcowe).
Nie chcesz, aby pilot wysyłał catsygnał do urządzenia końcowego.
Chcesz:
ssh host cat remote-file > local-file
Mógłbyś:
ssh -t host 'stty -opost; cat remote-file` > local-file
To by działało (oprócz omówionego powyżej przypadku zapisu do uszkodzenia stderr ), ale nawet to byłoby nieoptymalne, ponieważ działałaby niepotrzebna warstwa pseudoterminalna host.
Więcej zabawy:
$ ssh localhost echo | od -tx1
0000000 0a
0000001
DOBRZE.
$ ssh -t localhost echo | od -tx1
0000000 0d 0a
0000002
LF przetłumaczone na CRLF
$ ssh -t localhost 'stty -opost; echo' | od -tx1
0000000 0a
0000001
OK ponownie
$ ssh -t localhost 'stty olcuc; echo x'
X
Jest to kolejna forma przetwarzania końcowego, która może być wykonana przez dyscyplinę na linii terminalowej.
$ 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
sshodmawia poinformowania serwera, aby używał pseudo-terminala, gdy jego własne dane wejściowe nie są terminalem. Możesz to -ttjednak wymusić :
$ echo x | ssh -tt localhost 'stty -opost; echo' | od -tx1
0000000 x \r \n \n
0000004
Dyscyplina liniowa robi znacznie więcej po stronie wejściowej.
Tutaj echonie czyta danych wejściowych ani nie został poproszony o przesłanie tego, x\r\n\nwięc skąd to pochodzi? To jest lokalny echozdalny pseudo-terminal ( stty echo). sshSerwer jest karmienie x\ngo czytać od klienta do strony głównej zdalnego pseudo-terminala. A dyscyplina liniowa tego powtarza to z powrotem (przed stty opostbiegiem, dlatego widzimy CRLFa nie LF). Jest to niezależne od tego, czy zdalna aplikacja odczytuje coś ze standardowego wejścia, czy nie.
$ (sleep 1; printf '\03') | ssh -tt localhost 'trap "echo ouch" INT; sleep 2'
^Couch
0x3Postać jest jak echo ^C( ^a C) z powodu stty echoctla powłoka i spać otrzyma SIGINT bo stty isig.
Więc gdy:
ssh -t host cat remote-file > local-file
jest wystarczająco zły, ale
ssh -tt host 'cat > remote-file' < local-file
przesyłanie plików w drugą stronę jest znacznie gorsze. Dostaniesz kilka CR -> Tłumaczenia LF, ale również problemy z wszystkich znaków specjalnych ( ^C, ^Z, ^D, ^?, ^S...), a także pilot catnie będzie widoczne EOF, gdy koniec local-filezostanie osiągnięty tylko wtedy, gdy ^Djest wysyłany po \r, \nlub inny, ^Djak robisz cat > filew swoim terminalu.
-topcja, która przerywa transfer. Nie używaj-tlub-T, chyba że potrzebujesz ich z bardzo konkretnego powodu. Domyślnie działa w zdecydowanej większości przypadków, więc te opcje są bardzo rzadko potrzebne.