J'essaie de copier des fichiers sur SSH , mais je ne peux pas l'utiliser scp
car je ne connais pas le nom de fichier exact dont j'ai besoin. Bien que les petits fichiers binaires et les fichiers texte se transfèrent correctement, les gros fichiers binaires sont modifiés. Voici le fichier sur le serveur:
remote$ ls -la
-rw-rw-r-- 1 user user 244970907 Aug 24 11:11 foo.gz
remote$ md5sum foo.gz
9b5a44dad9d129bab52cbc6d806e7fda foo.gz
Voici le fichier après l'avoir déplacé:
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
Notez que le fichier téléchargé est plus gros que celui du serveur! Cependant, si je fais de même avec un très petit fichier, alors tout fonctionne comme prévu:
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
réel 0m3.041s utilisateur 0m0.013s sys 0m0.005s
local$ md5sum hi.txt.gz
08bf5080733d46a47d339520176b9211 hi.txt.gz
Les deux tailles de fichier sont de 26 octets dans ce cas.
Pourquoi les petits fichiers peuvent-ils être transférés correctement, mais les fichiers volumineux reçoivent des octets ajoutés?
-t
option qui rompt le transfert. N'utilisez pas-t
ou-T
, sauf si vous en avez besoin pour une raison très précise. La valeur par défaut fonctionne dans la grande majorité des cas, donc ces options sont très rarement nécessaires.ssh -t cat
c'est la seule façon de transférer des fichiers.Réponses:
TL; DR
Ne pas utiliser
-t
.-t
implique un pseudo-terminal sur l'hôte distant et ne doit être utilisé que pour exécuter des applications visuelles à partir d'un terminal.Explication
Le caractère de saut de ligne (également appelé retour à la ligne ou
\n
) est celui qui, lorsqu'il est envoyé à un terminal, indique au terminal de déplacer son curseur vers le bas.Pourtant, lorsque vous exécutez
seq 3
dans un terminal, c'est làseq
qu'écrit1\n2\n3\n
quelque chose comme/dev/pts/0
, vous ne voyez pas:mais
Pourquoi donc?
En fait, quand
seq 3
(oussh host seq 3
d'ailleurs) écrit1\n2\n3\n
, le terminal voit1\r\n2\r\n3\r\n
. Autrement dit, les sauts de ligne ont été traduits en retour chariot (sur lequel les terminaux déplacent leur curseur vers la gauche de l'écran) et en saut de ligne.Cela se fait par le pilote de périphérique terminal. Plus exactement, par la discipline de ligne du terminal (ou pseudo-terminal), un module logiciel qui réside dans le noyau.
Vous pouvez contrôler le comportement de cette discipline de ligne avec la
stty
commande. La traduction deLF
->CRLF
est activée avec(qui est généralement activé par défaut). Vous pouvez le désactiver avec:
Ou vous pouvez désactiver tous les traitements de sortie avec:
Si vous faites cela et exécutez
seq 3
, vous verrez alors:comme prévu.
Maintenant, quand vous le faites:
seq
n'écrit plus sur un terminal, il écrit dans un fichier, aucune traduction n'est en cours. Contientsome-file
donc1\n2\n3\n
. La traduction n'est effectuée que lors de l'écriture sur un terminal. Et cela n'est fait que pour l'affichage.de même, lorsque vous faites:
ssh
écrit1\n2\n3\n
quelle que soitssh
la sortie.Ce qui se passe réellement, c'est que la
seq 3
commande est exécutéehost
avec sa sortie standard redirigée vers un canal. Lessh
serveur sur l'hôte lit l'autre extrémité du canal et l'envoie sur le canal crypté à votressh
client et lessh
client l'écrit sur sa sortie standard, dans votre cas un périphérique pseudo-terminal, oùLF
s sont traduits enCRLF
pour être affichés.De nombreuses applications interactives se comportent différemment lorsque leur sortie standard n'est pas un terminal. Par exemple, si vous exécutez:
vi
ne l'aime pas, il n'aime pas que sa sortie passe dans un tuyau. Il pense qu'il ne parle pas à un appareil capable de comprendre les séquences d'échappement de positionnement du curseur par exemple.A donc
ssh
la-t
possibilité pour cela. Avec cette option, le serveur ssh sur l'hôte crée un périphérique pseudo-terminal et en fait la stdout (et stdin et stderr) devi
. Ce quivi
écrit sur ce terminal passe par cette discipline de ligne de pseudo-terminal distant et est lu par lessh
serveur et envoyé sur le canal crypté aussh
client. C'est la même chose qu'avant, sauf qu'au lieu d'utiliser un canal , lessh
serveur utilise un pseudo-terminal .L'autre différence est que du côté client, le
ssh
client met le terminal enraw
mode. Cela signifie qu'aucune traduction n'y est effectuée (opost
est désactivée et également d'autres comportements côté entrée). Par exemple, lorsque vous tapez Ctrl-C, au lieu d'interrompressh
, ce^C
caractère est envoyé vers le côté distant, où la discipline de ligne du pseudo-terminal distant envoie l' interruption à la commande distante.Quand vous faites:
seq 3
écrit1\n2\n3\n
sur sa sortie standard, qui est un périphérique pseudo-terminal. En raison deonlcr
, qui obtient traduit sur l' hôte pour1\r\n2\r\n3\r\n
et vous sera envoyé sur le canal crypté. De votre côté, il n'y a pas de traduction (onlcr
désactivée),1\r\n2\r\n3\r\n
est donc affiché intact (en raison duraw
mode) et correctement sur l'écran de votre émulateur de terminal.Maintenant, si vous le faites:
Il n'y a aucune différence d'en haut.
ssh
va écrire la même chose:,1\r\n2\r\n3\r\n
mais cette fois ensome-file
.Donc, fondamentalement, toutes les
LF
sorties deseq
ont été traduitesCRLF
ensome-file
.C'est la même chose si vous le faites:
Tous les
LF
caractères (0x0a octets) sont en cours de traduction en CRLF (0x0d 0x0a).C'est probablement la raison de la corruption de votre fichier. Dans le cas du deuxième fichier plus petit, il se trouve que le fichier ne contient pas d'octets 0x0a, il n'y a donc pas de corruption.
Notez que vous pouvez obtenir différents types de corruption avec différents paramètres tty. Un autre type potentiel de corruption associé à
-t
est si vos fichiers de démarrage surhost
(~/.bashrc
,~/.ssh/rc
...) écrivent des choses sur leur stderr, car avec-t
le stdout et le stderr du shell distant finissent par être fusionnés dansssh
le stdout de (ils vont tous les deux au pseudo - dispositif terminal).Vous ne voulez pas que la télécommande
cat
émette sur un périphérique terminal.Tu veux:
Vous pourriez faire:
Cela fonctionnerait (sauf dans l' écriture dans le cas de corruption stderr discuté ci-dessus), mais même cela serait sous-optimal car vous auriez cette couche pseudo-terminale inutile en cours d'exécution
host
.Un peu plus de plaisir:
D'ACCORD.
LF
traduit enCRLF
OK encore.
C'est une autre forme de post-traitement de sortie qui peut être effectuée par la discipline de la ligne terminale.
ssh
refuse de dire au serveur d'utiliser un pseudo-terminal lorsque sa propre entrée n'est pas un terminal. Vous pouvez cependant le forcer avec-tt
:La discipline de ligne en fait beaucoup plus du côté des entrées.
Ici,
echo
ne lit pas son entrée et n'a pas été invité à le sortir,x\r\n\n
alors d'où vient-il? C'est le localecho
du pseudo-terminal distant (stty echo
). Lessh
serveur alimente lax\n
lecture du client vers le côté maître du pseudo-terminal distant. Et la discipline de ligne qui en fait écho (avantstty opost
est exécutée, c'est pourquoi nous voyons unCRLF
et nonLF
). Cela est indépendant du fait que l'application distante lit ou non quoi que ce soit à partir de stdin.Le
0x3
caractère est renvoyé en écho comme^C
(^
etC
) à cause destty echoctl
et le shell et sleep reçoivent un SIGINT carstty isig
.Donc pendant:
est déjà assez mauvais, mais
transférer des fichiers dans l'autre sens est bien pire. Vous obtenez des CR -> traduction de LF, mais aussi des problèmes avec tous les caractères spéciaux (
^C
,^Z
,^D
,^?
,^S
...) ainsi que la télécommandecat
ne verra pas EOF lorsque la finlocal-file
est atteint, seulement quand^D
est envoyé après\r
,\n
ou un autre^D
comme lorsque vous faitescat > file
dans votre terminal.la source
Lorsque vous utilisez cette méthode pour copier le fichier, les fichiers semblent être différents.
Serveur distant
Serveur local
Exécuter votre
ssh ... cat
commande:Résultats dans ce fichier sur le serveur local:
Enquêter pourquoi?
L'étude du fichier résultant du côté local montre qu'il a été corrompu. Si vous retirez le
-t
commutateur de votressh
commande, cela fonctionne comme prévu.Les sommes de contrôle fonctionnent désormais aussi:
la source