TL; DR
No utilice -t. -timplica 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 3en una terminal, ahí es donde seqescribe 1\n2\n3\nalgo 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 3para 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 sttycomando. La traducción de LF-> CRLFse 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
seqya no está escribiendo en un terminal, está escribiendo en un archivo, no se está haciendo ninguna traducción. Entonces some-filecontiene 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
sshestá escribiendo 1\n2\n3\nindependientemente de a qué se sshdirige la salida.
Lo que realmente sucede es que el seq 3comando se ejecuta hostcon su stdout redirigido a una tubería. El sshservidor en el host lee el otro extremo de la tubería y lo envía a través del canal encriptado a su sshcliente y el sshcliente lo escribe en su stdout, en su caso, un dispositivo pseudo-terminal, donde LFse traducen CRLFpara 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
vino 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 sshtiene la -topció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 viescribe en ese dispositivo terminal pasa por esa disciplina remota de línea pseudo-terminal y es leída por el sshservidor y enviada por el canal encriptado al sshcliente. Es lo mismo que antes, excepto que en lugar de usar una tubería , el sshservidor usa un pseudo-terminal .
La otra diferencia es que en el lado del sshcliente , el cliente establece el terminal en rawmodo. Eso significa que no se realiza ninguna traducción allí ( opostestá deshabilitado y también otros comportamientos del lado de entrada). Por ejemplo, cuando escribe Ctrl-C, en lugar de interrumpir ssh, ese ^Ccará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 3escribe 1\n2\n3\nen su stdout, que es un dispositivo pseudo-terminal. Debido a onlcrque se traduce en el host para 1\r\n2\r\n3\r\nenviar a usted a través del canal cifrado. Por su parte, no hay traducción ( onlcrdeshabilitada), por lo que 1\r\n2\r\n3\r\nse muestra intacta (debido al rawmodo) 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. sshescribirá lo mismo: 1\r\n2\r\n3\r\npero esta vez en some-file.
Así que, básicamente, todo el LFen la salida de seqhaber sido traducida a CRLFdentro some-file.
Es lo mismo si haces:
ssh -t host cat remote-file > local-file
Todos los LFcaracteres (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 -tes si sus archivos de inicio en host( ~/.bashrc, ~/.ssh/rc...) escriben cosas en su stderr, porque con -tstdout y stderr del shell remoto terminan fusionándose en sshstdout (ambos van al pseudo -dispositivo terminal).
No desea que el control remoto se catenví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
sshse 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í, echono lee su entrada ni se le pide que la envíe, x\r\n\nentonces, ¿de dónde viene? Ese es el local echodel pseudo-terminal remoto ( stty echo). El sshservidor está enviando la x\nlectura del cliente al lado maestro del pseudo terminal remoto. Y la disciplina de línea de eso se repite (antes stty opostse ejecuta, por eso vemos a CRLFy 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 0x3carácter se repite como ^C( ^y C) debido a stty echoctly 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 catno verá EF cuando el fin de local-fileque se alcance, sólo cuando ^Dse envía después de una \r, \nu otro ^Dcomo cuando lo haces cat > fileen tu terminal.
-topción, que rompe la transferencia. No use-to-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.