TL; DR
Nicht verwenden -t. -tbeinhaltet ein Pseudoterminal auf dem Remote-Host und sollte nur zum Ausführen visueller Anwendungen von einem Terminal aus verwendet werden.
Erläuterung
Das Zeilenvorschubzeichen (auch als Zeilenvorschub oder als "Zeilenvorschub" bezeichnet \n) weist das Terminal an, den Cursor nach unten zu bewegen, wenn es an ein Terminal gesendet wird.
Wenn Sie jedoch seq 3in einem Terminal ausgeführt werden und dort auf etwas seqschreiben , sehen Sie nicht:1\n2\n3\n/dev/pts/0
1
2
3
aber
1
2
3
Warum das?
Tatsächlich sieht das Terminal , wenn seq 3(oder ssh host seq 3in diesem Fall) schreibt . Das heißt, die Zeilenvorschübe wurden in Wagenrücklauf (auf den Terminals den Cursor zurück nach links bewegen) und Zeilenvorschub übersetzt.1\n2\n3\n1\r\n2\r\n3\r\n
Das erledigt der Endgerätetreiber. Genauer gesagt, nach der Leitungsdisziplin des Endgeräts (oder Pseudo-Endgeräts) ein Softwaremodul, das sich im Kernel befindet.
Sie können das Verhalten dieser Zeilendisziplin mit dem sttyBefehl steuern . Die Übersetzung von LF-> CRLFwird mit eingeschaltet
stty onlcr
(die in der Regel standardmäßig aktiviert ist). Sie können es ausschalten mit:
stty -onlcr
Oder Sie können die gesamte Ausgabeverarbeitung ausschalten mit:
stty -opost
Wenn du das tust und rennst seq 3, siehst du:
$ stty -onlcr; seq 3
1
2
3
wie erwartet.
Nun, wenn Sie dies tun:
seq 3 > some-file
seqschreibt nicht mehr in ein Terminal, es wird in eine Datei geschrieben, es wird keine Übersetzung durchgeführt. Enthält some-filealso 1\n2\n3\n. Die Übersetzung erfolgt nur beim Schreiben auf ein Endgerät. Und es ist nur für die Anzeige gemacht.
in ähnlicher Weise, wenn Sie tun:
ssh host seq 3
sshschreibt, 1\n2\n3\nunabhängig davon ssh, wohin die Ausgabe geht.
Was tatsächlich passiert, ist, dass der seq 3Befehl hostmit seiner stdout ausgeführt wird, die an eine Pipe umgeleitet wird. Der sshServer auf dem Host liest das andere Ende der Pipe und sendet es über den verschlüsselten Kanal an Ihren sshClient. Der sshClient schreibt es auf seinen Standardausgang, in Ihrem Fall ein Pseudo-Endgerät, in das LFs CRLFzur Anzeige übersetzt werden.
Viele interaktive Anwendungen verhalten sich anders, wenn ihre Standardausgabe kein Terminal ist. Wenn Sie beispielsweise Folgendes ausführen:
ssh host vi
vimag es nicht, mag es nicht, wenn seine Ausgabe an eine Pipe geht. Es spricht nicht mit einem Gerät, das zum Beispiel die Escape-Sequenzen für die Cursorpositionierung versteht.
Also sshhat die -tOption dafür. Mit dieser Option erstellt der ssh-Server auf dem Host ein Pseudo-Terminal-Gerät und erstellt das stdout (und stdin und stderr) von vi. Was viauf diesem Endgerät schreibt, durchläuft diese Pseudo-Terminal-Leitungsdisziplin und wird vom sshServer gelesen und über den verschlüsselten Kanal an den sshClient gesendet . Es ist das gleiche wie zuvor , außer daß anstelle der Verwendung eines Rohres , das sshverwendet Server ein Pseudo-Terminal .
Der andere Unterschied besteht darin, dass der Client auf der sshClientseite das Terminal in den rawModus versetzt. Das bedeutet, dass dort keine Übersetzung durchgeführt wird ( opostist deaktiviert und auch andere eingabeseitige Verhaltensweisen). Wenn Sie beispielsweise ein Zeichen Ctrl-Ceingeben ssh, wird dieses ^CZeichen nicht unterbrochen, sondern an die entfernte Seite gesendet, wo die Leitungsdisziplin des entfernten Pseudoterminals den Interrupt an den entfernten Befehl sendet .
Wenn Sie das tun:
ssh -t host seq 3
seq 3schreibt 1\n2\n3\nauf sein stdout, das ein Pseudo-Terminal-Gerät ist. Wegen onlcr, die übersetzt wird auf dem Host zu 1\r\n2\r\n3\r\nund an Sie über den verschlüsselten Kanal. Auf Ihrer Seite gibt es keine Übersetzung ( onlcrdeaktiviert), daher 1\r\n2\r\n3\r\nwird diese (aufgrund des rawModus) unangetastet und korrekt auf dem Bildschirm Ihres Terminalemulators angezeigt.
Nun, wenn Sie dies tun:
ssh -t host seq 3 > some-file
Es gibt keinen Unterschied von oben. sshwerde das selbe schreiben:, 1\r\n2\r\n3\r\naber diesmal in some-file.
Also im Grunde alle LFin der Ausgabe seqhat übersetzt CRLFin some-file.
Es ist das gleiche, wenn Sie tun:
ssh -t host cat remote-file > local-file
Alle LFZeichen (0x0a Bytes) werden in CRLF (0x0d 0x0a) übersetzt.
Das ist wahrscheinlich der Grund für die Beschädigung in Ihrer Datei. Im Fall der zweiten kleineren Datei kommt es einfach so vor, dass die Datei keine 0x0a-Bytes enthält, sodass keine Beschädigung vorliegt.
Beachten Sie, dass Sie mit unterschiedlichen Einstellungen für tty verschiedene Arten von Beschädigungen erhalten können. Eine weitere mögliche Art der Beschädigung -tbesteht darin, dass Ihre Startdateien auf host( ~/.bashrc, ~/.ssh/rc...) Dinge in ihre stderr schreiben, da -tstdout und stderr der Remote-Shell in sshstdout zusammengeführt werden (beide gehen ins Pseudo Endgerät).
Die Fernbedienung catsoll dort nicht auf ein Endgerät ausgegeben werden.
Sie wollen:
ssh host cat remote-file > local-file
Du könntest es tun:
ssh -t host 'stty -opost; cat remote-file` > local-file
Das würde funktionieren (außer in dem oben diskutierten Fall der Korruption beim Schreiben an stderr ), aber selbst das wäre suboptimal, da Sie diese unnötige Pseudo-Terminal-Schicht hätten host.
Noch mehr Spaß:
$ ssh localhost echo | od -tx1
0000000 0a
0000001
OKAY.
$ ssh -t localhost echo | od -tx1
0000000 0d 0a
0000002
LF übersetzt nach CRLF
$ ssh -t localhost 'stty -opost; echo' | od -tx1
0000000 0a
0000001
Wieder ok.
$ ssh -t localhost 'stty olcuc; echo x'
X
Dies ist eine andere Form der Ausgabe-Nachbearbeitung, die von der Terminalleitungsdisziplin durchgeführt werden kann.
$ 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
sshWeigert sich, dem Server die Verwendung eines Pseudoterminals mitzuteilen, wenn seine eigene Eingabe kein Terminal ist. Du kannst es erzwingen mit -tt:
$ echo x | ssh -tt localhost 'stty -opost; echo' | od -tx1
0000000 x \r \n \n
0000004
Die Liniendisziplin leistet viel mehr auf der Eingabeseite.
Hier echowird weder die Eingabe gelesen noch aufgefordert, diese auszugeben. x\r\n\nWo kommt das her? Das ist das lokale echodes entfernten Pseudoterminals ( stty echo). Der sshServer leitet den x\nvom Client gelesenen Wert an die Masterseite des entfernten Pseudoterminals weiter. Und die Liniendisziplin von diesem spiegelt es wieder (bevor stty opostausgeführt wird, weshalb wir ein CRLFund nicht sehen LF). Das ist unabhängig davon, ob die Remote-Anwendung etwas von stdin liest oder nicht.
$ (sleep 1; printf '\03') | ssh -tt localhost 'trap "echo ouch" INT; sleep 2'
^Couch
Das 0x3Zeichen wird als ^C( ^und C) zurückgemeldet, stty echoctlund die Shell und der Schlaf erhalten ein ZEICHEN, weil stty isig.
Also, während:
ssh -t host cat remote-file > local-file
ist schon schlimm genug, aber
ssh -tt host 'cat > remote-file' < local-file
Dateien in die andere Richtung zu übertragen ist viel schlimmer. Sie werden einige CR bekommen -> LF Übersetzung, aber auch Probleme mit allen Sonderzeichen ( ^C, ^Z, ^D, ^?, ^S...) und auch die Fernbedienung catnicht EOF sehen , wenn das Ende local-fileerreicht ist, nur dann , wenn ^Dnach einem gesendet wird \r, \noder ein anderes ^Dwie cat > filein Ihrem Terminal.
-tOption, die die Übertragung unterbricht. Verwenden Sie nicht-toder-T, es sei denn, Sie benötigen sie aus einem bestimmten Grund. Die Standardeinstellung funktioniert in den allermeisten Fällen, sodass diese Optionen sehr selten benötigt werden.