TL; DR
Jangan gunakan -t. -tmelibatkan 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 3di terminal, di situlah seqmenulis 1\n2\n3\nsesuatu seperti /dev/pts/0, Anda tidak melihat:
1
2
3
tapi
1
2
3
Mengapa demikian?
Sebenarnya, ketika seq 3(atau ssh host seq 3dalam hal ini) menulis 1\n2\n3\n, terminal melihat 1\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 sttyperintah. Terjemahan LF-> CRLFdihidupkan dengan
stty onlcr
(yang umumnya diaktifkan secara default). Anda dapat mematikannya dengan:
stty -onlcr
Atau Anda dapat mematikan semua pemrosesan output dengan:
stty -opost
Jika Anda melakukannya dan menjalankannya seq 3, Anda akan melihat:
$ stty -onlcr; seq 3
1
2
3
seperti yang diharapkan.
Sekarang, ketika Anda melakukannya:
seq 3 > some-file
seqtidak lagi menulis ke terminal, itu menulis ke file, tidak ada terjemahan yang dilakukan. Begitu some-filejuga mengandung 1\n2\n3\n. Terjemahan hanya dilakukan saat menulis ke perangkat terminal. Dan itu hanya dilakukan untuk tampilan.
sama halnya, ketika Anda melakukannya:
ssh host seq 3
sshmenulis 1\n2\n3\napa pun sshkeluarannya.
Apa yang sebenarnya terjadi adalah bahwa seq 3perintah dijalankan hostdengan stdout diarahkan ke sebuah pipa. The sshserver pada tuan membaca ujung pipa dan mengirimkannya melalui saluran terenkripsi untuk Anda sshklien dan sshklien menulis itu ke stdout, dalam kasus Anda perangkat pseudo-terminal, di manaLF dijabarkan ke CRLFuntuk ditampilkan.
Banyak aplikasi interaktif berperilaku berbeda ketika stdout mereka bukan terminal. Misalnya, jika Anda menjalankan:
ssh host vi
vitidak suka, tidak suka outputnya ke pipa. Ia berpikir itu tidak berbicara dengan perangkat yang dapat memahami urutan pelarian posisi kursor misalnya.
Jadi sshada -tpilihan untuk itu. Dengan opsi itu, server ssh pada host menciptakan perangkat pseudo-terminal dan menjadikan stdout (dan stdin, dan stderr) dari vi. Apa yang vimenulis pada perangkat terminal melewati disiplin garis pseudo-terminal jarak jauh dan dibaca oleh sshserver dan dikirim melalui saluran terenkripsi ke sshklien. Itu sama seperti sebelumnya kecuali bahwa alih-alih menggunakan pipa , sshserver menggunakan pseudo-terminal .
Perbedaan lainnya adalah pada sisi klien, sshklien mengatur terminal dalam rawmode. Itu berarti bahwa tidak ada terjemahan yang dilakukan di sana ( opostdinonaktifkan dan juga perilaku sisi input lainnya). Misalnya, ketika Anda mengetik Ctrl-C, alih-alih menyela ssh, ^Ckarakter itu dikirim ke sisi jarak jauh, di mana garis disiplin terminal pseudo jarak jauh mengirimkan interupsi ke perintah jarak jauh.
Saat kamu melakukan:
ssh -t host seq 3
seq 3menulis 1\n2\n3\nke stdout-nya, yang merupakan perangkat pseudo-terminal. Karenanya onlcr, itu diterjemahkan pada host ke 1\r\n2\r\n3\r\ndan dikirim kepada Anda melalui saluran terenkripsi. Di pihak Anda tidak ada terjemahan ( onlcrdinonaktifkan), sehingga 1\r\n2\r\n3\r\nditampilkan tidak tersentuh (karenaraw mode) dan dengan benar di layar emulator terminal Anda.
Sekarang, jika Anda melakukannya:
ssh -t host seq 3 > some-file
Tidak ada perbedaan dari atas. sshakan menulis hal yang sama:, 1\r\n2\r\n3\r\ntetapi kali ini menjadi some-file.
Jadi pada dasarnya semua LFdalam output seqtelah diterjemahkan ke CRLFdalam some-file.
Itu sama jika Anda melakukannya:
ssh -t host cat remote-file > local-file
Semua LFkarakter (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 -tadalah jika file startup Anda di host( ~/.bashrc, ~/.ssh/rc...) menulis sesuatu ke stderr mereka, karena dengan -tstdout dan stderr dari remote shell akhirnya digabung menjadi sshstdout (mereka berdua pergi ke pseudo perangkat -terminal).
Anda tidak ingin remote cat untuk output ke perangkat terminal di sana.
Kamu ingin:
ssh host cat remote-file > local-file
Anda bisa melakukannya:
ssh -t host 'stty -opost; cat remote-file` > local-file
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:
$ ssh localhost echo | od -tx1
0000000 0a
0000001
BAIK.
$ ssh -t localhost echo | od -tx1
0000000 0d 0a
0000002
LF diterjemahkan ke CRLF
$ ssh -t localhost 'stty -opost; echo' | od -tx1
0000000 0a
0000001
OKE lagi.
$ ssh -t localhost 'stty olcuc; echo x'
X
Itu bentuk lain dari post-processing output yang dapat dilakukan oleh disiplin jalur 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
sshmenolak untuk memberi tahu server untuk menggunakan pseudo-terminal ketika inputnya sendiri bukan terminal. Anda dapat memaksanya dengan -tt:
$ echo x | ssh -tt localhost 'stty -opost; echo' | od -tx1
0000000 x \r \n \n
0000004
Disiplin garis lebih banyak melakukan di sisi input.
Di sini, echotidak membaca inputnya atau diminta untuk output itu x\r\n\njadi dari mana asalnya? Itu adalah lokal echodari pseudo-terminal jarak jauh ( stty echo). The sshserver makan x\nitu dibaca dari klien ke sisi master dari pseudo-remote terminal. Dan garis disiplin itu menggemakannya kembali (sebelum stty opostdijalankan itulah sebabnya kita melihat a CRLFdan tidak LF). Itu terlepas dari apakah aplikasi jarak jauh membaca sesuatu dari stdin atau tidak.
$ (sleep 1; printf '\03') | ssh -tt localhost 'trap "echo ouch" INT; sleep 2'
^Couch
The 0x3karakter bergema kembali sebagai ^C( ^dan C) karena stty echoctldan shell dan tidur menerima SIGINT karena stty isig.
Jadi sementara:
ssh -t host cat remote-file > local-file
sudah cukup buruk, tapi
ssh -tt host 'cat > remote-file' < local-file
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 remote cattidak akan melihat eof ketika akhir local-filetercapai, hanya ketika ^Ddikirim setelah \r, \natau lainnya ^Dseperti ketika melakukan cat > filedi terminal Anda.
-tpilihan, yang memecah transfer. Jangan gunakan-tatau-T, kecuali Anda membutuhkannya untuk alasan yang sangat spesifik. Defaultnya berfungsi di sebagian besar kasus, sehingga opsi tersebut sangat jarang dibutuhkan.