Próbuję skopiować pliki przez SSH , ale nie mogę ich użyć, scp
ponieważ nie znam dokładnej nazwy pliku, której potrzebuję. Chociaż małe pliki binarne i pliki tekstowe przesyłają się dobrze, duże pliki binarne ulegają zmianie. Oto plik na serwerze:
remote$ ls -la
-rw-rw-r-- 1 user user 244970907 Aug 24 11:11 foo.gz
remote$ md5sum foo.gz
9b5a44dad9d129bab52cbc6d806e7fda foo.gz
Oto plik po przeniesieniu go:
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
Pamiętaj, że pobrany plik jest większy niż ten na serwerze! Jeśli jednak zrobię to samo z bardzo małym plikiem, wszystko działa zgodnie z oczekiwaniami:
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
rzeczywisty 0m3.041s użytkownik 0m0.013s sys 0m0.005s
local$ md5sum hi.txt.gz
08bf5080733d46a47d339520176b9211 hi.txt.gz
Oba rozmiary plików mają w tym przypadku 26 bajtów.
Dlaczego małe pliki mogą być przesyłane dobrze, ale do dużych plików dodawane są niektóre bajty?
-t
opcja, która przerywa transfer. Nie używaj-t
lub-T
, chyba że potrzebujesz ich z bardzo konkretnego powodu. Domyślnie działa w zdecydowanej większości przypadków, więc te opcje są bardzo rzadko potrzebne.ssh -t cat
jest to jedyny sposób przesyłania plików.Odpowiedzi:
TL; DR
Nie używać
-t
.-t
obejmuje pseudo-terminal na zdalnym hoście i powinien być używany tylko do uruchamiania aplikacji wizualnych z terminala.Wyjaśnienie
Znak przesunięcia linii (znany również jako znak nowej linii lub
\n
) to ten, który po wysłaniu do terminala informuje terminal o przesunięciu kursora w dół.Jednak podczas uruchamiania
seq 3
w terminalu, czyli tam, gdzieseq
pisze1\n2\n3\n
coś takiego/dev/pts/0
, nie widzisz:ale
Dlaczego?
W rzeczywistości, kiedy
seq 3
(lubssh host seq 3
w tym przypadku) pisze1\n2\n3\n
, terminal widzi1\r\n2\r\n3\r\n
. Oznacza to, że przesunięcia linii zostały przetłumaczone na powrót karetki (po której terminale przesuwają kursor z powrotem na lewą stronę ekranu) i przesunięcie linii.Odbywa się to przez sterownik urządzenia końcowego. Dokładniej, według dyscypliny liniowej urządzenia terminalowego (lub pseudo-terminalnego), moduł oprogramowania, który znajduje się w jądrze.
Za pomocą polecenia można kontrolować zachowanie tej dyscypliny liniowej
stty
. TłumaczenieLF
->CRLF
jest włączone za pomocą(który zazwyczaj jest domyślnie włączony). Możesz to wyłączyć za pomocą:
Lub możesz wyłączyć wszystkie przetwarzanie danych wyjściowych za pomocą:
Jeśli to zrobisz i uruchomisz
seq 3
, zobaczysz:zgodnie z oczekiwaniami.
Teraz, kiedy to zrobisz:
seq
nie zapisuje już na terminalu, zapisuje do pliku, tłumaczenie nie jest wykonywane. Więcsome-file
zawiera1\n2\n3\n
. Tłumaczenie jest wykonywane tylko podczas pisania na urządzeniu końcowym. I to tylko na pokaz.podobnie, gdy robisz:
ssh
pisze1\n2\n3\n
niezależnie od tego, do czegossh
zmierza wyjście.W rzeczywistości dzieje się tak, że
seq 3
polecenie jest uruchamianehost
z przekierowaniem standardu na potok.ssh
Serwera hosta odczytuje na drugi koniec rury i wysłać go na zaszyfrowany kanał dossh
klienta, assh
klient zapisuje go na jego standardowe wyjście, w przypadku urządzenia pseudo-terminali, gdzieLF
s są tłumaczone naCRLF
na wyświetlaczu.Wiele interaktywnych aplikacji zachowuje się inaczej, gdy ich standardowe wyjście nie jest terminalem. Na przykład, jeśli uruchomisz:
vi
nie lubi tego, nie lubi, aby jego wyjście trafiało do potoku. Uważa, że nie rozmawia z urządzeniem, które może na przykład zrozumieć sekwencje specjalne pozycjonowania kursora.Tak też
ssh
jest-t
opcja. Dzięki tej opcji serwer ssh na hoście tworzy pseudoterminalowe urządzenie i sprawia, że stdout (i stdin i stderr) zvi
. To, covi
pisze na tym urządzeniu końcowym, przechodzi przez tę dyscyplinę zdalnej linii pseudoterminalowej i jest odczytywane przezssh
serwer i wysyłane zaszyfrowanym kanałem dossh
klienta. To samo, jak poprzednio z tym, że zamiast stosowania rury Thessh
serwera wykorzystuje pseudoterminal .Inna różnica polega na tym, że po stronie
ssh
klienta klient ustawia terminal wraw
trybie. Oznacza to, że nie wykonuje się tam tłumaczenia (opost
jest wyłączone, a także inne zachowania po stronie wejściowej). Na przykład, gdy piszesz Ctrl-C, zamiast przerywaniassh
,^C
znak ten jest wysyłany do strony zdalnej, gdzie dyscyplina liniowa zdalnego pseudo-terminala wysyła przerwanie do polecenia zdalnego.Kiedy to zrobisz:
seq 3
zapisuje1\n2\n3\n
na standardowe wyjście, które jest pseudoterminalnym urządzeniem. Ze względuonlcr
, że zostanie przetłumaczony na hosta do1\r\n2\r\n3\r\n
i wysłane przez szyfrowany kanał. Po twojej stronie nie ma tłumaczenia (onlcr
wyłączone), więc1\r\n2\r\n3\r\n
jest wyświetlane nietknięte (z powoduraw
trybu) i poprawnie na ekranie emulatora terminala.Teraz, jeśli to zrobisz:
Nie ma różnicy z góry.
ssh
napisze to samo:1\r\n2\r\n3\r\n
ale tym razem wsome-file
.Więc w zasadzie wszystkie dane
LF
wyjścioweseq
zostały przetłumaczoneCRLF
nasome-file
.To samo, jeśli wykonasz:
Wszystkie
LF
znaki (0x0a bajtów) są tłumaczone na CRLF (0x0d 0x0a).Prawdopodobnie jest to przyczyną uszkodzenia pliku. W przypadku drugiego mniejszego pliku dzieje się tak, że plik nie zawiera bajtów 0x0a, więc nie ma uszkodzenia.
Pamiętaj, że możesz uzyskać różne rodzaje uszkodzenia przy różnych ustawieniach tty. Innym potencjalnym rodzajem uszkodzenia związanego z tym
-t
jest to, że pliki startowe nahost
(~/.bashrc
,~/.ssh/rc
...) zapisują rzeczy do swojego stderr, ponieważ wraz-t
ze stdout i stderr zdalnej powłoki są scalane wssh
stdout (oba idą do pseudo - urządzenie końcowe).Nie chcesz, aby pilot wysyłał
cat
sygnał do urządzenia końcowego.Chcesz:
Mógłbyś:
To by działało (oprócz omówionego powyżej przypadku zapisu do uszkodzenia stderr ), ale nawet to byłoby nieoptymalne, ponieważ działałaby niepotrzebna warstwa pseudoterminalna
host
.Więcej zabawy:
DOBRZE.
LF
przetłumaczone naCRLF
OK ponownie
Jest to kolejna forma przetwarzania końcowego, która może być wykonana przez dyscyplinę na linii terminalowej.
ssh
odmawia poinformowania serwera, aby używał pseudo-terminala, gdy jego własne dane wejściowe nie są terminalem. Możesz to-tt
jednak wymusić :Dyscyplina liniowa robi znacznie więcej po stronie wejściowej.
Tutaj
echo
nie czyta danych wejściowych ani nie został poproszony o przesłanie tego,x\r\n\n
więc skąd to pochodzi? To jest lokalnyecho
zdalny pseudo-terminal (stty echo
).ssh
Serwer jest karmieniex\n
go czytać od klienta do strony głównej zdalnego pseudo-terminala. A dyscyplina liniowa tego powtarza to z powrotem (przedstty opost
biegiem, dlatego widzimyCRLF
a nieLF
). Jest to niezależne od tego, czy zdalna aplikacja odczytuje coś ze standardowego wejścia, czy nie.0x3
Postać jest jak echo^C
(^
aC
) z powodustty echoctl
a powłoka i spać otrzyma SIGINT bostty isig
.Więc gdy:
jest wystarczająco zły, ale
przesyłanie plików w drugą stronę jest znacznie gorsze. Dostaniesz kilka CR -> Tłumaczenia LF, ale również problemy z wszystkich znaków specjalnych (
^C
,^Z
,^D
,^?
,^S
...), a także pilotcat
nie będzie widoczne EOF, gdy konieclocal-file
zostanie osiągnięty tylko wtedy, gdy^D
jest wysyłany po\r
,\n
lub inny,^D
jak robiszcat > file
w swoim terminalu.źródło
Gdy używasz tej metody do skopiowania pliku, pliki wydają się być inne.
Zdalny serwer
Serwer lokalny
Uruchamianie
ssh ... cat
polecenia:Wyniki w tym pliku na serwerze lokalnym:
Badanie dlaczego?
Badanie wynikowego pliku po stronie lokalnej pokazuje, że został uszkodzony. Jeśli usuniesz
-t
przełącznik zssh
polecenia, będzie działał zgodnie z oczekiwaniami.Sumy kontrolne teraz też działają:
źródło