Saya mencoba menyalin file melalui SSH , tetapi tidak dapat digunakan scp
karena tidak mengetahui nama file yang saya butuhkan. Meskipun file biner kecil dan file teks dapat ditransfer dengan baik, file biner besar dapat diubah. Ini file di server:
remote$ ls -la
-rw-rw-r-- 1 user user 244970907 Aug 24 11:11 foo.gz
remote$ md5sum foo.gz
9b5a44dad9d129bab52cbc6d806e7fda foo.gz
Ini file setelah saya memindahkannya:
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
Perhatikan bahwa file yang diunduh lebih besar daripada yang ada di server! Namun, jika saya melakukan hal yang sama dengan file yang sangat kecil, maka semuanya berfungsi seperti yang diharapkan:
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
0m3.041s nyata pengguna 0m0.013s sys 0m0.005s
local$ md5sum hi.txt.gz
08bf5080733d46a47d339520176b9211 hi.txt.gz
Kedua ukuran file adalah 26 byte dalam hal ini.
Mengapa file kecil dapat ditransfer dengan baik, tetapi file besar mendapatkan beberapa byte ditambahkan ke dalamnya?
-t
pilihan, yang memecah transfer. Jangan gunakan-t
atau-T
, kecuali Anda membutuhkannya untuk alasan yang sangat spesifik. Defaultnya berfungsi di sebagian besar kasus, sehingga opsi tersebut sangat jarang dibutuhkan.ssh -t cat
itu satu-satunya cara untuk mentransfer file.Jawaban:
TL; DR
Jangan gunakan
-t
.-t
melibatkan pseudo-terminal pada host jarak jauh dan seharusnya hanya digunakan untuk menjalankan aplikasi visual dari terminal.Penjelasan
Karakter linefeed (juga dikenal sebagai baris baru atau
\n
) adalah karakter yang ketika dikirim ke terminal memberitahu terminal untuk memindahkan kursor ke bawah.Namun, ketika Anda menjalankan
seq 3
di terminal, di situlahseq
menulis1\n2\n3\n
sesuatu seperti/dev/pts/0
, Anda tidak melihat:tapi
Mengapa demikian?
Sebenarnya, ketika
seq 3
(ataussh host seq 3
dalam hal ini) menulis1\n2\n3\n
, terminal melihat1\r\n2\r\n3\r\n
. Yaitu, umpan baris telah diterjemahkan ke carriage-return (tempat terminal memindahkan kursornya kembali ke kiri layar) dan umpan baris.Itu dilakukan oleh driver perangkat terminal. Lebih tepatnya, dengan disiplin garis perangkat terminal (atau pseudo-terminal), modul perangkat lunak yang berada di kernel.
Anda dapat mengontrol perilaku disiplin garis itu dengan
stty
perintah. TerjemahanLF
->CRLF
dihidupkan dengan(yang umumnya diaktifkan secara default). Anda dapat mematikannya dengan:
Atau Anda dapat mematikan semua pemrosesan output dengan:
Jika Anda melakukannya dan menjalankannya
seq 3
, Anda akan melihat:seperti yang diharapkan.
Sekarang, ketika Anda melakukannya:
seq
tidak lagi menulis ke terminal, itu menulis ke file, tidak ada terjemahan yang dilakukan. Begitusome-file
juga mengandung1\n2\n3\n
. Terjemahan hanya dilakukan saat menulis ke perangkat terminal. Dan itu hanya dilakukan untuk tampilan.sama halnya, ketika Anda melakukannya:
ssh
menulis1\n2\n3\n
apa punssh
keluarannya.Apa yang sebenarnya terjadi adalah bahwa
seq 3
perintah dijalankanhost
dengan stdout diarahkan ke sebuah pipa. Thessh
server pada tuan membaca ujung pipa dan mengirimkannya melalui saluran terenkripsi untuk Andassh
klien danssh
klien menulis itu ke stdout, dalam kasus Anda perangkat pseudo-terminal, di manaLF
dijabarkan keCRLF
untuk ditampilkan.Banyak aplikasi interaktif berperilaku berbeda ketika stdout mereka bukan terminal. Misalnya, jika Anda menjalankan:
vi
tidak suka, tidak suka outputnya ke pipa. Ia berpikir itu tidak berbicara dengan perangkat yang dapat memahami urutan pelarian posisi kursor misalnya.Jadi
ssh
ada-t
pilihan untuk itu. Dengan opsi itu, server ssh pada host menciptakan perangkat pseudo-terminal dan menjadikan stdout (dan stdin, dan stderr) darivi
. Apa yangvi
menulis pada perangkat terminal melewati disiplin garis pseudo-terminal jarak jauh dan dibaca olehssh
server dan dikirim melalui saluran terenkripsi kessh
klien. Itu sama seperti sebelumnya kecuali bahwa alih-alih menggunakan pipa ,ssh
server menggunakan pseudo-terminal .Perbedaan lainnya adalah pada sisi klien,
ssh
klien mengatur terminal dalamraw
mode. Itu berarti bahwa tidak ada terjemahan yang dilakukan di sana (opost
dinonaktifkan dan juga perilaku sisi input lainnya). Misalnya, ketika Anda mengetik Ctrl-C, alih-alih menyelassh
,^C
karakter itu dikirim ke sisi jarak jauh, di mana garis disiplin terminal pseudo jarak jauh mengirimkan interupsi ke perintah jarak jauh.Saat kamu melakukan:
seq 3
menulis1\n2\n3\n
ke stdout-nya, yang merupakan perangkat pseudo-terminal. Karenanyaonlcr
, itu diterjemahkan pada host ke1\r\n2\r\n3\r\n
dan dikirim kepada Anda melalui saluran terenkripsi. Di pihak Anda tidak ada terjemahan (onlcr
dinonaktifkan), sehingga1\r\n2\r\n3\r\n
ditampilkan tidak tersentuh (karenaraw
mode) dan dengan benar di layar emulator terminal Anda.Sekarang, jika Anda melakukannya:
Tidak ada perbedaan dari atas.
ssh
akan menulis hal yang sama:,1\r\n2\r\n3\r\n
tetapi kali ini menjadisome-file
.Jadi pada dasarnya semua
LF
dalam outputseq
telah diterjemahkan keCRLF
dalamsome-file
.Itu sama jika Anda melakukannya:
Semua
LF
karakter (0x0a byte) sedang diterjemahkan ke dalam CRLF (0x0d 0x0a).Mungkin itulah alasan kerusakan pada file Anda. Dalam kasus file kedua yang lebih kecil, kebetulan file tersebut tidak mengandung 0x0a byte, sehingga tidak ada korupsi.
Perhatikan bahwa Anda bisa mendapatkan berbagai jenis korupsi dengan pengaturan tty yang berbeda. Jenis korupsi potensial lainnya yang terkait dengan
-t
adalah jika file startup Anda dihost
(~/.bashrc
,~/.ssh/rc
...) menulis sesuatu ke stderr mereka, karena dengan-t
stdout dan stderr dari remote shell akhirnya digabung menjadissh
stdout (mereka berdua pergi ke pseudo perangkat -terminal).Anda tidak ingin remote
cat
untuk output ke perangkat terminal di sana.Kamu ingin:
Anda bisa melakukannya:
Itu akan bekerja (kecuali dalam penulisan kasus korupsi stderr yang dibahas di atas), tetapi bahkan itu akan menjadi kurang optimal karena Anda akan memiliki lapisan pseudo-terminal yang tidak perlu dijalankan
host
.Lebih menyenangkan:
BAIK.
LF
diterjemahkan keCRLF
OKE lagi.
Itu bentuk lain dari post-processing output yang dapat dilakukan oleh disiplin jalur terminal.
ssh
menolak untuk memberi tahu server untuk menggunakan pseudo-terminal ketika inputnya sendiri bukan terminal. Anda dapat memaksanya dengan-tt
:Disiplin garis lebih banyak melakukan di sisi input.
Di sini,
echo
tidak membaca inputnya atau diminta untuk output itux\r\n\n
jadi dari mana asalnya? Itu adalah lokalecho
dari pseudo-terminal jarak jauh (stty echo
). Thessh
server makanx\n
itu dibaca dari klien ke sisi master dari pseudo-remote terminal. Dan garis disiplin itu menggemakannya kembali (sebelumstty opost
dijalankan itulah sebabnya kita melihat aCRLF
dan tidakLF
). Itu terlepas dari apakah aplikasi jarak jauh membaca sesuatu dari stdin atau tidak.The
0x3
karakter bergema kembali sebagai^C
(^
danC
) karenastty echoctl
dan shell dan tidur menerima SIGINT karenastty isig
.Jadi sementara:
sudah cukup buruk, tapi
mentransfer file dengan cara sebaliknya jauh lebih buruk. Anda akan mendapatkan beberapa CR -> terjemahan LF, tetapi juga masalah dengan semua karakter khusus (
^C
,^Z
,^D
,^?
,^S
...) dan juga remotecat
tidak akan melihat eof ketika akhirlocal-file
tercapai, hanya ketika^D
dikirim setelah\r
,\n
atau lainnya^D
seperti ketika melakukancat > file
di terminal Anda.sumber
Saat menggunakan metode itu untuk menyalin file, file-file tersebut tampak berbeda.
Server jarak jauh
Server lokal
Menjalankan
ssh ... cat
perintah Anda :Hasil dalam file ini di server lokal:
Menyelidiki mengapa?
Menyelidiki file yang dihasilkan di sisi lokal menunjukkan bahwa itu sudah rusak. Jika Anda
-t
beralih darissh
perintah Anda maka itu berfungsi seperti yang diharapkan.Checksum sekarang juga berfungsi:
sumber