TL; DR
Nicht verwenden -t
. -t
beinhaltet 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 3
in einem Terminal ausgeführt werden und dort auf etwas seq
schreiben , 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 3
in 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\n
1\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 stty
Befehl steuern . Die Übersetzung von LF
-> CRLF
wird 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
seq
schreibt nicht mehr in ein Terminal, es wird in eine Datei geschrieben, es wird keine Übersetzung durchgeführt. Enthält some-file
also 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
ssh
schreibt, 1\n2\n3\n
unabhängig davon ssh
, wohin die Ausgabe geht.
Was tatsächlich passiert, ist, dass der seq 3
Befehl host
mit seiner stdout ausgeführt wird, die an eine Pipe umgeleitet wird. Der ssh
Server auf dem Host liest das andere Ende der Pipe und sendet es über den verschlüsselten Kanal an Ihren ssh
Client. Der ssh
Client schreibt es auf seinen Standardausgang, in Ihrem Fall ein Pseudo-Endgerät, in das LF
s CRLF
zur Anzeige übersetzt werden.
Viele interaktive Anwendungen verhalten sich anders, wenn ihre Standardausgabe kein Terminal ist. Wenn Sie beispielsweise Folgendes ausführen:
ssh host vi
vi
mag 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 ssh
hat die -t
Option 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 vi
auf diesem Endgerät schreibt, durchläuft diese Pseudo-Terminal-Leitungsdisziplin und wird vom ssh
Server gelesen und über den verschlüsselten Kanal an den ssh
Client gesendet . Es ist das gleiche wie zuvor , außer daß anstelle der Verwendung eines Rohres , das ssh
verwendet Server ein Pseudo-Terminal .
Der andere Unterschied besteht darin, dass der Client auf der ssh
Clientseite das Terminal in den raw
Modus versetzt. Das bedeutet, dass dort keine Übersetzung durchgeführt wird ( opost
ist deaktiviert und auch andere eingabeseitige Verhaltensweisen). Wenn Sie beispielsweise ein Zeichen Ctrl-Ceingeben ssh
, wird dieses ^C
Zeichen 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 3
schreibt 1\n2\n3\n
auf sein stdout, das ein Pseudo-Terminal-Gerät ist. Wegen onlcr
, die übersetzt wird auf dem Host zu 1\r\n2\r\n3\r\n
und an Sie über den verschlüsselten Kanal. Auf Ihrer Seite gibt es keine Übersetzung ( onlcr
deaktiviert), daher 1\r\n2\r\n3\r\n
wird diese (aufgrund des raw
Modus) 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. ssh
werde das selbe schreiben:, 1\r\n2\r\n3\r\n
aber diesmal in some-file
.
Also im Grunde alle LF
in der Ausgabe seq
hat übersetzt CRLF
in some-file
.
Es ist das gleiche, wenn Sie tun:
ssh -t host cat remote-file > local-file
Alle LF
Zeichen (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 -t
besteht darin, dass Ihre Startdateien auf host
( ~/.bashrc
, ~/.ssh/rc
...) Dinge in ihre stderr schreiben, da -t
stdout und stderr der Remote-Shell in ssh
stdout zusammengeführt werden (beide gehen ins Pseudo Endgerät).
Die Fernbedienung cat
soll 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
ssh
Weigert 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 echo
wird weder die Eingabe gelesen noch aufgefordert, diese auszugeben. x\r\n\n
Wo kommt das her? Das ist das lokale echo
des entfernten Pseudoterminals ( stty echo
). Der ssh
Server leitet den x\n
vom Client gelesenen Wert an die Masterseite des entfernten Pseudoterminals weiter. Und die Liniendisziplin von diesem spiegelt es wieder (bevor stty opost
ausgeführt wird, weshalb wir ein CRLF
und 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 0x3
Zeichen wird als ^C
( ^
und C
) zurückgemeldet, stty echoctl
und 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 cat
nicht EOF sehen , wenn das Ende local-file
erreicht ist, nur dann , wenn ^D
nach einem gesendet wird \r
, \n
oder ein anderes ^D
wie cat > file
in Ihrem Terminal.
-t
Option, die die Übertragung unterbricht. Verwenden Sie nicht-t
oder-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.