TL; DR
No utilice -t
. -t
implica un pseudo-terminal en el host remoto y solo debe usarse para ejecutar aplicaciones visuales desde un terminal.
Explicación
El carácter de salto de línea (también conocido como nueva línea o \n
) es el que cuando se envía a un terminal le dice al terminal que mueva el cursor hacia abajo.
Sin embargo, cuando se ejecuta seq 3
en una terminal, ahí es donde seq
escribe 1\n2\n3\n
algo así /dev/pts/0
, no ve:
1
2
3
pero
1
2
3
¿Porqué es eso?
En realidad, cuando seq 3
(o ssh host seq 3
para el caso) escribe 1\n2\n3\n
, el terminal ve 1\r\n2\r\n3\r\n
. Es decir, los avances de línea se han traducido en retorno de carro (sobre el cual los terminales mueven su cursor hacia la izquierda de la pantalla) y avance de línea.
Eso lo hace el controlador del dispositivo terminal. Más exactamente, por la disciplina de línea del dispositivo terminal (o pseudo-terminal), un módulo de software que reside en el núcleo.
Puede controlar el comportamiento de esa disciplina de línea con el stty
comando. La traducción de LF
-> CRLF
se activa con
stty onlcr
(que generalmente está habilitado de forma predeterminada). Puedes apagarlo con:
stty -onlcr
O puede desactivar todo el procesamiento de salida con:
stty -opost
Si haces eso y corres seq 3
, verás:
$ stty -onlcr; seq 3
1
2
3
como se esperaba.
Ahora, cuando lo haces:
seq 3 > some-file
seq
ya no está escribiendo en un terminal, está escribiendo en un archivo, no se está haciendo ninguna traducción. Entonces some-file
contiene 1\n2\n3\n
. La traducción solo se realiza cuando se escribe en un dispositivo terminal. Y solo se hace para mostrar.
de manera similar, cuando haces:
ssh host seq 3
ssh
está escribiendo 1\n2\n3\n
independientemente de a qué se ssh
dirige la salida.
Lo que realmente sucede es que el seq 3
comando se ejecuta host
con su stdout redirigido a una tubería. El ssh
servidor en el host lee el otro extremo de la tubería y lo envía a través del canal encriptado a su ssh
cliente y el ssh
cliente lo escribe en su stdout, en su caso, un dispositivo pseudo-terminal, donde LF
se traducen CRLF
para su visualización.
Muchas aplicaciones interactivas se comportan de manera diferente cuando su stdout no es un terminal. Por ejemplo, si ejecutas:
ssh host vi
vi
no le gusta, no le gusta que su salida vaya a una tubería. Cree, por ejemplo, que no está hablando con un dispositivo que puede entender las secuencias de escape de posicionamiento del cursor.
Entonces ssh
tiene la -t
opción para eso. Con esa opción, el servidor ssh en el host crea un dispositivo pseudo-terminal y lo convierte en stdout (y stdin y stderr) de vi
. Lo que vi
escribe en ese dispositivo terminal pasa por esa disciplina remota de línea pseudo-terminal y es leída por el ssh
servidor y enviada por el canal encriptado al ssh
cliente. Es lo mismo que antes, excepto que en lugar de usar una tubería , el ssh
servidor usa un pseudo-terminal .
La otra diferencia es que en el lado del ssh
cliente , el cliente establece el terminal en raw
modo. Eso significa que no se realiza ninguna traducción allí ( opost
está deshabilitado y también otros comportamientos del lado de entrada). Por ejemplo, cuando escribe Ctrl-C, en lugar de interrumpir ssh
, ese ^C
carácter se envía al lado remoto, donde la disciplina de línea del pseudo terminal remoto envía la interrupción al comando remoto.
Cuando tu lo hagas:
ssh -t host seq 3
seq 3
escribe 1\n2\n3\n
en su stdout, que es un dispositivo pseudo-terminal. Debido a onlcr
que se traduce en el host para 1\r\n2\r\n3\r\n
enviar a usted a través del canal cifrado. Por su parte, no hay traducción ( onlcr
deshabilitada), por lo que 1\r\n2\r\n3\r\n
se muestra intacta (debido al raw
modo) y correctamente en la pantalla de su emulador de terminal.
Ahora, si lo haces:
ssh -t host seq 3 > some-file
No hay diferencia desde arriba. ssh
escribirá lo mismo: 1\r\n2\r\n3\r\n
pero esta vez en some-file
.
Así que, básicamente, todo el LF
en la salida de seq
haber sido traducida a CRLF
dentro some-file
.
Es lo mismo si haces:
ssh -t host cat remote-file > local-file
Todos los LF
caracteres (0x0a bytes) se están traduciendo a CRLF (0x0d 0x0a).
Esa es probablemente la razón de la corrupción en su archivo. En el caso del segundo archivo más pequeño, sucede que el archivo no contiene 0x0a bytes, por lo que no hay corrupción.
Tenga en cuenta que podría obtener diferentes tipos de corrupción con diferentes configuraciones de tty. Otro tipo potencial de corrupción asociada -t
es si sus archivos de inicio en host
( ~/.bashrc
, ~/.ssh/rc
...) escriben cosas en su stderr, porque con -t
stdout y stderr del shell remoto terminan fusionándose en ssh
stdout (ambos van al pseudo -dispositivo terminal).
No desea que el control remoto se cat
envíe a un dispositivo terminal allí.
Usted quiere:
ssh host cat remote-file > local-file
Podrías hacerlo:
ssh -t host 'stty -opost; cat remote-file` > local-file
Eso funcionaría (excepto en el caso de corrupción escrito por stderr discutido anteriormente), pero incluso eso sería subóptimo, ya que tendría esa capa pseudo-terminal innecesaria ejecutándose host
.
Un poco más divertido:
$ ssh localhost echo | od -tx1
0000000 0a
0000001
OKAY.
$ ssh -t localhost echo | od -tx1
0000000 0d 0a
0000002
LF
traducido a CRLF
$ ssh -t localhost 'stty -opost; echo' | od -tx1
0000000 0a
0000001
OK otra vez
$ ssh -t localhost 'stty olcuc; echo x'
X
Esa es otra forma de procesamiento posterior de salida que puede realizar la disciplina de línea terminal.
$ 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
se niega a decirle al servidor que use un pseudo terminal cuando su propia entrada no es un terminal. Sin embargo, puedes forzarlo con -tt
:
$ echo x | ssh -tt localhost 'stty -opost; echo' | od -tx1
0000000 x \r \n \n
0000004
La disciplina de línea hace mucho más en el lado de entrada.
Aquí, echo
no lee su entrada ni se le pide que la envíe, x\r\n\n
entonces, ¿de dónde viene? Ese es el local echo
del pseudo-terminal remoto ( stty echo
). El ssh
servidor está enviando la x\n
lectura del cliente al lado maestro del pseudo terminal remoto. Y la disciplina de línea de eso se repite (antes stty opost
se ejecuta, por eso vemos a CRLF
y no LF
). Eso es independiente de si la aplicación remota lee algo de stdin o no.
$ (sleep 1; printf '\03') | ssh -tt localhost 'trap "echo ouch" INT; sleep 2'
^Couch
El 0x3
carácter se repite como ^C
( ^
y C
) debido a stty echoctl
y el shell y el sueño reciben un SIGINT porque stty isig
.
Entonces mientras:
ssh -t host cat remote-file > local-file
es bastante malo, pero
ssh -tt host 'cat > remote-file' < local-file
transferir archivos al revés es mucho peor. Usted obtendrá algunos CR -> Traducción LF, sino también problemas con todos los caracteres especiales ( ^C
, ^Z
, ^D
, ^?
, ^S
...) y también el control remoto cat
no verá EF cuando el fin de local-file
que se alcance, sólo cuando ^D
se envía después de una \r
, \n
u otro ^D
como cuando lo haces cat > file
en tu terminal.
-t
opción, que rompe la transferencia. No use-t
o-T
, a menos que los necesite por una razón muy específica. El valor predeterminado funciona en la gran mayoría de los casos, por lo que esas opciones rara vez se necesitan.