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 situlah seq
menulis 1\n2\n3\n
sesuatu seperti /dev/pts/0
, Anda tidak melihat:
1
2
3
tapi
1
2
3
Mengapa demikian?
Sebenarnya, ketika seq 3
(atau ssh host seq 3
dalam 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 stty
perintah. Terjemahan LF
-> CRLF
dihidupkan 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
seq
tidak lagi menulis ke terminal, itu menulis ke file, tidak ada terjemahan yang dilakukan. Begitu some-file
juga 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
ssh
menulis 1\n2\n3\n
apa pun ssh
keluarannya.
Apa yang sebenarnya terjadi adalah bahwa seq 3
perintah dijalankan host
dengan stdout diarahkan ke sebuah pipa. The ssh
server pada tuan membaca ujung pipa dan mengirimkannya melalui saluran terenkripsi untuk Anda ssh
klien dan ssh
klien menulis itu ke stdout, dalam kasus Anda perangkat pseudo-terminal, di manaLF
dijabarkan ke CRLF
untuk ditampilkan.
Banyak aplikasi interaktif berperilaku berbeda ketika stdout mereka bukan terminal. Misalnya, jika Anda menjalankan:
ssh host vi
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) dari vi
. Apa yang vi
menulis pada perangkat terminal melewati disiplin garis pseudo-terminal jarak jauh dan dibaca oleh ssh
server dan dikirim melalui saluran terenkripsi ke ssh
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 dalam raw
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 menyela ssh
, ^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:
ssh -t host seq 3
seq 3
menulis 1\n2\n3\n
ke stdout-nya, yang merupakan perangkat pseudo-terminal. Karenanya onlcr
, itu diterjemahkan pada host ke 1\r\n2\r\n3\r\n
dan dikirim kepada Anda melalui saluran terenkripsi. Di pihak Anda tidak ada terjemahan ( onlcr
dinonaktifkan), sehingga 1\r\n2\r\n3\r\n
ditampilkan 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. ssh
akan menulis hal yang sama:, 1\r\n2\r\n3\r\n
tetapi kali ini menjadi some-file
.
Jadi pada dasarnya semua LF
dalam output seq
telah diterjemahkan ke CRLF
dalam some-file
.
Itu sama jika Anda melakukannya:
ssh -t host cat remote-file > local-file
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 di host
( ~/.bashrc
, ~/.ssh/rc
...) menulis sesuatu ke stderr mereka, karena dengan -t
stdout dan stderr dari remote shell akhirnya digabung menjadi ssh
stdout (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
ssh
menolak 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, echo
tidak membaca inputnya atau diminta untuk output itu x\r\n\n
jadi dari mana asalnya? Itu adalah lokal echo
dari pseudo-terminal jarak jauh ( stty echo
). The ssh
server makan x\n
itu dibaca dari klien ke sisi master dari pseudo-remote terminal. Dan garis disiplin itu menggemakannya kembali (sebelum stty opost
dijalankan itulah sebabnya kita melihat a CRLF
dan 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 0x3
karakter bergema kembali sebagai ^C
( ^
dan C
) karena stty echoctl
dan 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 cat
tidak akan melihat eof ketika akhir local-file
tercapai, hanya ketika ^D
dikirim setelah \r
, \n
atau lainnya ^D
seperti ketika melakukan cat > file
di terminal Anda.
-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.