Estoy tratando de copiar archivos a través de SSH , pero no puedo usarlos scp
porque 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 [email protected] -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 [email protected] -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?
-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.ssh -t cat
es la única forma de transferir archivos.Respuestas:
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 dondeseq
escribe1\n2\n3\n
algo así/dev/pts/0
, no ve:pero
¿Porqué es eso?
En realidad, cuando
seq 3
(ossh host seq 3
para el caso) escribe1\n2\n3\n
, el terminal ve1\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 deLF
->CRLF
se activa con(que generalmente está habilitado de forma predeterminada). Puedes apagarlo con:
O puede desactivar todo el procesamiento de salida con:
Si haces eso y corres
seq 3
, verás:como se esperaba.
Ahora, cuando lo haces:
seq
ya no está escribiendo en un terminal, está escribiendo en un archivo, no se está haciendo ninguna traducción. Entoncessome-file
contiene1\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
está escribiendo1\n2\n3\n
independientemente de a qué sessh
dirige la salida.Lo que realmente sucede es que el
seq 3
comando se ejecutahost
con su stdout redirigido a una tubería. Elssh
servidor en el host lee el otro extremo de la tubería y lo envía a través del canal encriptado a sussh
cliente y elssh
cliente lo escribe en su stdout, en su caso, un dispositivo pseudo-terminal, dondeLF
se traducenCRLF
para su visualización.Muchas aplicaciones interactivas se comportan de manera diferente cuando su stdout no es un terminal. Por ejemplo, si ejecutas:
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) devi
. Lo quevi
escribe en ese dispositivo terminal pasa por esa disciplina remota de línea pseudo-terminal y es leída por elssh
servidor y enviada por el canal encriptado alssh
cliente. Es lo mismo que antes, excepto que en lugar de usar una tubería , elssh
servidor usa un pseudo-terminal .La otra diferencia es que en el lado del
ssh
cliente , el cliente establece el terminal enraw
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 interrumpirssh
, 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:
seq 3
escribe1\n2\n3\n
en su stdout, que es un dispositivo pseudo-terminal. Debido aonlcr
que se traduce en el host para1\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 que1\r\n2\r\n3\r\n
se muestra intacta (debido alraw
modo) y correctamente en la pantalla de su emulador de terminal.Ahora, si lo haces:
No hay diferencia desde arriba.
ssh
escribirá lo mismo:1\r\n2\r\n3\r\n
pero esta vez ensome-file
.Así que, básicamente, todo el
LF
en la salida deseq
haber sido traducida aCRLF
dentrosome-file
.Es lo mismo si haces:
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 enhost
(~/.bashrc
,~/.ssh/rc
...) escriben cosas en su stderr, porque con-t
stdout y stderr del shell remoto terminan fusionándose enssh
stdout (ambos van al pseudo -dispositivo terminal).No desea que el control remoto se
cat
envíe a un dispositivo terminal allí.Usted quiere:
Podrías hacerlo:
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:
OKAY.
LF
traducido aCRLF
OK otra vez
Esa es otra forma de procesamiento posterior de salida que puede realizar la disciplina de línea terminal.
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
: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 localecho
del pseudo-terminal remoto (stty echo
). Elssh
servidor está enviando lax\n
lectura del cliente al lado maestro del pseudo terminal remoto. Y la disciplina de línea de eso se repite (antesstty opost
se ejecuta, por eso vemos aCRLF
y noLF
). Eso es independiente de si la aplicación remota lee algo de stdin o no.El
0x3
carácter se repite como^C
(^
yC
) debido astty echoctl
y el shell y el sueño reciben un SIGINT porquestty isig
.Entonces mientras:
es bastante malo, pero
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 remotocat
no verá EF cuando el fin delocal-file
que se alcance, sólo cuando^D
se envía después de una\r
,\n
u otro^D
como cuando lo hacescat > file
en tu terminal.fuente
Cuando se usa ese método para copiar el archivo, los archivos parecen ser diferentes.
Servidor remoto
Servidor local
Ejecutando su
ssh ... cat
comando:Resultados en este archivo en el servidor local:
¿Investigando por qué?
Investigar el archivo resultante en el lado local muestra que ha sido dañado. Si quita el
-t
interruptor de sussh
comando, entonces funciona como se esperaba.Las sumas de verificación ahora también funcionan:
fuente