¿Por qué se cambia este archivo binario a través de "ssh -t" que se cambia?


29

Estoy tratando de copiar archivos a través de SSH , pero no puedo usarlos scpporque no conozco el nombre de archivo exacto que necesito. Aunque los archivos binarios pequeños y los archivos de texto se transfieren bien, los archivos binarios grandes se alteran. Aquí está el archivo en el servidor:

remote$ ls -la
-rw-rw-r--  1 user user 244970907 Aug 24 11:11 foo.gz
remote$ md5sum foo.gz 
9b5a44dad9d129bab52cbc6d806e7fda foo.gz

Aquí está el archivo después de moverlo:

local$ time ssh me@server.com -t 'cat /path/to/foo.gz' > latest.gz

real    1m52.098s
user    0m2.608s
sys     0m4.370s
local$ md5sum latest.gz
76fae9d6a4711bad1560092b539d034b  latest.gz

local$ ls -la
-rw-rw-r--  1 dotancohen dotancohen 245849912 Aug 24 18:26 latest.gz

¡Tenga en cuenta que el archivo descargado es más grande que el del servidor! Sin embargo, si hago lo mismo con un archivo muy pequeño, entonces todo funciona como se esperaba:

remote$ echo "Hello" | gzip -c > hello.txt.gz
remote$ md5sum hello.txt.gz
08bf5080733d46a47d339520176b9211  hello.txt.gz

local$ time ssh me@server.com -t 'cat /path/to/hello.txt.gz' > hi.txt.gz

usuario real 0m3.041s 0m0.013s sys 0m0.005s

local$ md5sum hi.txt.gz
08bf5080733d46a47d339520176b9211  hi.txt.gz

Ambos tamaños de archivo son 26 bytes en este caso.

¿Por qué los archivos pequeños pueden transferirse bien, pero los archivos grandes obtienen algunos bytes?


10
Es la -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.
kasperd

3
Nunca pensé que diría esto en este siglo, pero es posible que desee probar uuencode y uudecode si ssh -t cates la única forma de transferir archivos.
Mark Plotnick

1
@MarkPlotnick, la versión moderna de uuencode / uudecode ahora se llama base64 / base64 -d
Archemar

Respuestas:


60

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.


5

Cuando se usa ese método para copiar el archivo, los archivos parecen ser diferentes.

Servidor remoto

ls -l | grep vim_cfg
-rw-rw-r--.  1 slm slm 9783257 Aug  5 16:51 vim_cfg.tgz

Servidor local

Ejecutando su ssh ... catcomando:

$ ssh dufresne -t 'cat ~/vim_cfg.tgz' > vim_cfg.tgz

Resultados en este archivo en el servidor local:

$ ls -l | grep vim_cfg.tgz 
-rw-rw-r--. 1 saml saml 9820481 Aug 24 12:13 vim_cfg.tgz

¿Investigando por qué?

Investigar el archivo resultante en el lado local muestra que ha sido dañado. Si quita el -tinterruptor de su sshcomando, entonces funciona como se esperaba.

$ ssh dufresne 'cat ~/vim_cfg.tgz' > vim_cfg.tgz

$ ls -l | grep vim_cfg.tgz
-rw-rw-r--. 1 saml saml 9783257 Aug 24 12:17 vim_cfg.tgz

Las sumas de verificación ahora también funcionan:

# remote server
$ ssh dufresne "md5sum ~/vim_cfg.tgz"
9e70b036836dfdf2871e76b3636a72c6  /home/slm/vim_cfg.tgz

# local server
$ md5sum vim_cfg.tgz 
9e70b036836dfdf2871e76b3636a72c6  vim_cfg.tgz

Gracias Sim Aunque de hecho fuiste el primero en publicar la respuesta correcta, seleccioné a Stéphane para la respuesta elegida debido a la profundidad de su explicación. No se preocupe, tiene un largo historial de publicaciones del que estoy aprendiendo y, por supuesto, he votado a favor de las publicaciones de las que aprendo. Gracias.
dotancohen

@dotancohen: no te preocupes, aceptas las A que crees que son las que más te ayudan como OP 8-). Sus habilidades para explicar por qué suceden las cosas no tienen rival, excepto por Gilles.
slm
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.