Mengapa tombol Enter tidak mengirim EOL?


19

Unix / Linux EOL adalah LF, linefeed, ASCII 10, escape sequence \n.

Berikut cuplikan Python untuk mendapatkan persis satu penekanan tombol:

import sys, tty, termios
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
    tty.setraw(sys.stdin.fileno())
    ch = sys.stdin.read(1)
finally:
    termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
    return ch

Ketika saya menekan Enterkeyboard saya untuk menanggapi potongan ini, itu memberi \r, carriage return, ASCII 13.

Di Windows , Enterkirim CR LF == 13 10. * nix bukan Windows; mengapa Entermemberi 13 bukan 10?


Coba baca dua byte.
Michael Hampton

@MichaelHampton Tidak, tidak ada yang menunggu descriptor file setelah satu byte dibaca
cat

Jawaban:


11

Sementara jawaban Thomas Dickey cukup benar, Stéphane Chazelas dengan benar disebutkan dalam komentar atas jawaban Dickey bahwa pertobatannya tidak dilakukan dengan keras; itu adalah bagian dari garis disiplin.

Bahkan, terjemahannya sepenuhnya dapat diprogram.

Halaman manual man 3 termios pada dasarnya berisi semua informasi terkait. (Tautan ke proyek man-pages Linux , yang menyebutkan fitur mana yang hanya Linux, dan yang umum untuk POSIX atau sistem lain; selalu periksa bagian Menyesuaian untuk pada setiap halaman di sana.)

The iflagatribut terminal ( old_settings[0]dalam kode yang ditunjukkan di pertanyaan di Python ) memiliki tiga bendera yang relevan pada semua sistem POSIXy:

  • INLCR: Jika diatur, terjemahkan NL ke CR pada input
  • ICRNL: Jika diatur (dan IGNCRtidak diatur), terjemahkan CR ke NL pada input
  • IGNCR: Abaikan CR pada input

Demikian pula, ada pengaturan keluaran terkait ( old_settings[1]), juga:

  • OPOST: Aktifkan pemrosesan keluaran.
  • OCRNL: Peta CR ke NL pada output.
  • ONLCR: Peta NL ke CR pada output. (XSI; tidak tersedia di semua sistem POSIX atau Single-Unix-Specification.)
  • ONOCR: Lewati (jangan keluaran) CR di kolom pertama.
  • ONLRET: Lewati (jangan keluaran) CR.

Misalnya, Anda dapat menghindari mengandalkan ttymodul. Operasi "makeraw" hanya membersihkan satu set bendera (dan mengatur CS8oflag):

import sys
import termios

fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
ch = None

try:
    new_settings = termios.tcgetattr(fd)
    new_settings[0] = new_settings[0] & ~termios.IGNBRK
    new_settings[0] = new_settings[0] & ~termios.BRKINT
    new_settings[0] = new_settings[0] & ~termios.PARMRK
    new_settings[0] = new_settings[0] & ~termios.ISTRIP
    new_settings[0] = new_settings[0] & ~termios.INLCR
    new_settings[0] = new_settings[0] & ~termios.IGNCR
    new_settings[0] = new_settings[0] & ~termios.ICRNL
    new_settings[0] = new_settings[0] & ~termios.IXON
    new_settings[1] = new_settings[1] & ~termios.OPOST
    new_settings[2] = new_settings[2] & ~termios.CSIZE
    new_settings[2] = new_settings[2] | termios.CS8
    new_settings[2] = new_settings[2] & ~termios.PARENB
    new_settings[3] = new_settings[3] & ~termios.ECHO
    new_settings[3] = new_settings[3] & ~termios.ECHONL
    new_settings[3] = new_settings[3] & ~termios.ICANON
    new_settings[3] = new_settings[3] & ~termios.ISIG
    new_settings[3] = new_settings[3] & ~termios.IEXTEN
    termios.tcsetattr(fd, termios.TCSANOW, new_settings)
finally:
    termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)

return ch

meskipun demi kompatibilitas, Anda mungkin ingin memeriksa apakah semua konstanta itu ada di modul termios terlebih dahulu (jika Anda menjalankan sistem non-POSIX). Anda juga dapat menggunakan new_settings[6][termios.VMIN]dan new_settings[6][termios.VTIME]untuk mengatur apakah pembacaan akan memblokir jika tidak ada data yang tertunda, dan berapa lama (dalam bilangan bulat deciseconds). (Biasanya VMINdiatur ke 0, dan VTIMEke 0 jika bacaan harus segera kembali, atau ke angka positif (sepersepuluh detik) berapa lama bacaan paling banyak menunggu.)

Seperti yang Anda lihat, di atas (dan "makeraw" secara umum) menonaktifkan semua terjemahan pada input, yang menjelaskan perilaku yang dilihat kucing:

    new_settings[0] = new_settings[0] & ~termios.INLCR
    new_settings[0] = new_settings[0] & ~termios.ICRNL
    new_settings[0] = new_settings[0] & ~termios.IGNCR

Untuk mendapatkan perilaku normal, cukup hapus baris yang menghapus ketiga baris tersebut, dan terjemahan input tidak berubah bahkan ketika "mentah".

The new_settings[1] = new_settings[1] & ~termios.OPOSTgaris menonaktifkan semua proses output, terlepas apa bendera keluaran lain mengatakan. Anda bisa menghilangkannya agar proses pemrosesan output tetap utuh. Ini membuat output "normal" bahkan dalam mode mentah. (Itu tidak mempengaruhi apakah input secara otomatis digaungkan atau tidak; yang dikendalikan oleh ECHOcflag in new_settings[3].)

Akhirnya, ketika atribut baru diatur, panggilan akan berhasil jika ada pengaturan baru yang ditetapkan. Jika pengaturannya sensitif - misalnya, jika Anda meminta kata sandi pada baris perintah -, Anda harus mendapatkan pengaturan baru, dan memverifikasi bahwa tanda-tanda penting sudah diatur / tidak disetel dengan benar, untuk memastikan.

Jika Anda ingin melihat pengaturan terminal Anda saat ini, jalankan

stty -a

Bendera input biasanya di baris keempat, dan bendera output di baris kelima, dengan -mendahului nama bendera jika bendera tidak disetel. Misalnya, outputnya bisa

speed 38400 baud; rows 58; columns 205; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?; eol2 = M-^?; swtch = M-^?; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 hupcl -cstopb cread -clocal -crtscts
-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc ixany imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke

Pada pseudoterminals, dan perangkat USB TTY, baud rate tidak relevan.

Jika Anda menulis skrip Bash yang ingin dibaca misalnya kata sandi, pertimbangkan idiom berikut:

#!/bin/bash
trap 'stty sane ; stty '"$(stty -g)" EXIT
stty -echo -echonl -imaxbel -isig -icanon min 1 time 0

The EXITperangkap dijalankan setiap kali shell keluar. The stty -gmembaca pengaturan saat ini terminal pada awal script, sehingga pengaturan saat dikembalikan ketika keluar skrip, secara otomatis. Anda bahkan dapat mengganggu skrip dengan Ctrl+ C, dan itu akan melakukan hal yang benar. (Dalam beberapa kasus sudut dengan sinyal, saya telah menemukan bahwa terminal kadang-kadang macet dengan pengaturan mentah / noncanonical (memerlukan satu untuk mengetik reset+ Entersecara membabi buta di terminal), tetapi berjalan stty sanesebelum mengembalikan pengaturan asli yang sebenarnya telah sembuh bahwa setiap kali untuk saya. Jadi itu sebabnya ada di sana; semacam keamanan tambahan.)

Anda dapat membaca jalur input (tidak bertanda ke terminal) menggunakan readbash built-in, atau bahkan membaca input karakter demi karakter menggunakan

IFS=$'\0'
input=""
while read -N 1 c ; do
    [[ "$c" == "" || "$c" == $'\n' || "$c" == $'\r' ]] && break
    input="$input$c"
done

Jika Anda tidak disetel IFSke ASCII NUL, readbuilt-in akan mengkonsumsi separator, sehingga cakan kosong. Perangkap untuk pemain muda.


1
Oh, demi Tuhan, tidak ada yang pernah sederhana :(
kucing

Saya menerima jawaban ini karena ini sangat membantu saya sebagai dev Python, meskipun yang lain bagus
cat

2
@ kucing: Meskipun ini mungkin paling membantu Anda, saya masih mengatakan jawaban Thomas Dickey lebih benar . Saya lebih suka Anda menerimanya sebagai gantinya.
Hewan Nominal

4
Sementara kesediaan Anda untuk melupakan +15 perwakilan Anda memberi Anda kredit, @cat cukup tepat. Apakah jawaban diterima atau tidak, bukan indikasi bahwa itu adalah "paling benar" dari jawaban yang diposting. Ini hanya berarti itu yang OP lebih suka untuk alasan pribadi apa pun. "Yang paling benar" biasanya yang paling tinggi mutunya. Menerima jawaban adalah pilihan pribadi, jika OP lebih memilih milikmu, tidak ada alasan untuk tidak menerimanya.
terdon

1
@terdon: Oke, kalau begitu saya dikoreksi.
Hewan Nominal

30

Intinya "karena sudah dilakukan seperti itu sejak mesin tik manual". Sangat.

Mesin tik manual memiliki carriage tempat kertas diumpankan, dan bergerak maju saat Anda mengetik (memuat pegas), dan memiliki tuas atau kunci yang akan melepaskan carriage, membiarkan pegas mengembalikan carriage ke margin kiri.

Saat entri data elektronik (teletype, dll) diperkenalkan, mereka meneruskannya. Jadi Enterkunci pada banyak terminal akan diberi label Return.

Umpan garis terjadi (dalam proses manual) setelah mengembalikan carriage ke margin kiri. Sekali lagi, perangkat elektronik meniru perangkat manual, membuat line-feedoperasi terpisah .

Kedua operasi dikodekan (untuk memungkinkan teletype menjadi lebih dari perangkat mandiri yang menciptakan jenis kertas), jadi kami memiliki CR(carriage-return) dan LF(line-feed). Gambar ini dari ASR 33 Informasi Teletype menunjukkan keyboard, dengan Returndi sisi kanan, dan Line-Feedhanya ke kiri. Berada di kanan , itu adalah kunci utama:

masukkan deskripsi gambar di sini

Unix datang kemudian. Pengembangnya suka mempersingkat hal-hal (lihat semua singkatan, bahkan creatuntuk "buat"). Dihadapkan dengan proses dua bagian yang mungkin, mereka memutuskan bahwa umpan garis hanya masuk akal jika mereka didahului dengan carriage-return. Jadi mereka menjatuhkan pengembalian carriage eksplisit dari file , dan menerjemahkan Returnkunci terminal untuk mengirim umpan baris yang sesuai. Hanya untuk menghindari kebingungan, mereka menyebut umpan baris sebagai "baris baru".

Saat menulis teks pada terminal, Unix menerjemahkan ke arah lain: umpan baris menjadi carriage-return / line-feed.

(Yaitu, "normal": apa yang disebut "mode matang", berbeda dengan mode "mentah" di mana tidak ada terjemahan yang dilakukan).

Ringkasan:

  • carriage-return / line-feed adalah urutan 13 10
  • yang perangkat mengirimkan 13 (karena "selamanya" dalam istilah Anda)
  • Sistem seperti Unix mengubahnya menjadi 13 10
  • Sistem lain tidak selalu menyimpan hanya 10 (Windows sebagian besar hanya menerima 10 atau 13 10, tergantung betapa pentingnya kompatibilitas).

1
Saya mencari gambar yang bagus untuk menunjukkan tuas untuk mesin tik manual, tetapi hanya menemukan gambar beresolusi rendah.
Thomas Dickey

3
Jika Anda harus mengetikkan salah satunya, Anda juga akan menyingkat semuanya!
Michael Hampton

3
Mengenai bagian histori: mesin tik manual yang saya gunakan dalam penggunaan saya, mirip dengan yang satu ini hanya memiliki satu tuas. Ketika Anda menariknya, pertama-tama memutar roller (umpan garis) dan kemudian hanya menarik kereta. Dan tarikan inilah yang mengisi pegas. Setiap huruf yang diketik, atau tab ditekan, akan melepaskan pegas agak, memindahkan kereta kembali ke posisi "dibongkar", yang berada di akhir baris, bukan awal.
RealSkeptic

2
Pada input, CR diterjemahkan (oleh disiplin garis tty) ke LF, bukan CR LF. Ada pada output (termasuk gema input) yang LF diterjemahkan ke CR LF. Saat Anda mengetik foo<Return>dalam mode matang, aplikasi membaca foo\ndan foo\r\ndikirim kembali oleh disiplin garis untuk gema ke terminal.
Stéphane Chazelas

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.