ในขณะที่คำตอบของโทมัสผ้ากันเปื้อนค่อนข้างถูกต้องStéphane Chazelas ถูกกล่าวถึงอย่างถูกต้องในความคิดเห็นของคำตอบของผ้ากันเปื้อนว่าการแปลงไม่ได้อยู่ในหิน; มันเป็นส่วนหนึ่งของวินัยในสายงาน
ในความเป็นจริงการแปลเป็นโปรแกรมอย่างสมบูรณ์
หน้าคน 3 termiosหน้าคนมีพื้นข้อมูลที่เกี่ยวข้องทั้งหมด (ลิงก์ใช้กับโครงการ man-page ของ Linuxซึ่งพูดถึงคุณลักษณะที่เป็น Linux เท่านั้นและเป็นเรื่องธรรมดาสำหรับ POSIX หรือระบบอื่น ๆ ให้ตรวจสอบส่วนที่สอดคล้องกับแต่ละหน้าเสมอ)
iflag
แอตทริบิวต์ขั้ว ( old_settings[0]
ในรหัสที่แสดงในคำถามในPython ) มีสามธงที่เกี่ยวข้องในทุกระบบ POSIXy:
INLCR
: หากตั้งไว้ให้แปล NL เป็น CR บนอินพุท
ICRNL
: หากตั้งค่า (และIGNCR
ไม่ได้ตั้งค่า) ให้แปล CR เป็น NL บนอินพุต
IGNCR
: ละเว้น CR บนอินพุต
ในทำนองเดียวกันมีการตั้งค่าผลลัพธ์ที่เกี่ยวข้อง ( old_settings[1]
) เช่นกัน:
OPOST
: เปิดใช้งานการประมวลผลเอาต์พุต
OCRNL
: แม็พ CR กับ NL บนเอาต์พุต
ONLCR
: แมป NL กับ CR บนเอาต์พุต (XSI; ไม่สามารถใช้ได้ในระบบ POSIX หรือ Single-Unix-Specification ทั้งหมด)
ONOCR
: ข้าม (ห้ามเอาต์พุต) CR ในคอลัมน์แรก
ONLRET
: ข้าม (ห้ามเอาต์พุต) CR
ตัวอย่างเช่นคุณสามารถหลีกเลี่ยงการพึ่งพาtty
โมดูล การดำเนินการ "makeraw" เพียงแค่ล้างชุดของธง (และชุดCS8
oflag):
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
แม้ว่าเพื่อความเข้ากันได้คุณอาจต้องการตรวจสอบว่าค่าคงที่ทั้งหมดนั้นมีอยู่ในโมดูล termios ก่อนหรือไม่ (ถ้าคุณใช้ระบบที่ไม่ใช่ POSIX) นอกจากนี้คุณยังสามารถใช้new_settings[6][termios.VMIN]
และnew_settings[6][termios.VTIME]
ตั้งค่าว่าการอ่านจะบล็อกหรือไม่หากไม่มีข้อมูลที่รอดำเนินการและระยะเวลา (เป็นจำนวนเต็มของ deciseconds) (โดยปกติแล้วVMIN
จะตั้งค่าเป็น 0 และVTIME
เป็น 0 หากผู้อ่านควรกลับมาทันทีหรือเป็นจำนวนบวก (สิบวินาที) ระยะเวลาที่การอ่านควรรอนานที่สุด)
ดังที่คุณเห็นข้างต้น (และ "makeraw" โดยทั่วไป) ปิดใช้งานการแปลทั้งหมดในอินพุตซึ่งจะอธิบายพฤติกรรมที่แมวเห็น:
new_settings[0] = new_settings[0] & ~termios.INLCR
new_settings[0] = new_settings[0] & ~termios.ICRNL
new_settings[0] = new_settings[0] & ~termios.IGNCR
ในการรับพฤติกรรมปกติเพียงแค่ละเว้นบรรทัดที่เคลียร์ทั้งสามบรรทัดและการแปลอินพุตจะไม่เปลี่ยนแปลงแม้เมื่อ "ดิบ"
new_settings[1] = new_settings[1] & ~termios.OPOST
เส้นปิดการใช้งานการประมวลผลการส่งออกทั้งหมดโดยไม่คำนึงถึงสิ่งที่ธงส่งออกอื่น ๆ บอกว่า คุณสามารถละเว้นมันเพื่อให้การประมวลผลเอาต์พุตยังคงอยู่ สิ่งนี้จะทำให้เอาต์พุต "ปกติ" แม้อยู่ในโหมด raw (ไม่ส่งผลกระทบต่อว่าอินพุตถูก echoed โดยอัตโนมัติหรือไม่นั้นถูกควบคุมโดยECHO
cflag ในnew_settings[3]
)
สุดท้ายเมื่อมีการตั้งค่าแอตทริบิวต์ใหม่การโทรจะสำเร็จถ้ามีการตั้งค่าใหม่ใด ๆ หากการตั้งค่ามีความละเอียดอ่อน - ตัวอย่างเช่นหากคุณขอรหัสผ่านในบรรทัดคำสั่ง - คุณควรได้รับการตั้งค่าใหม่และตรวจสอบว่าการตั้งค่าสถานะที่สำคัญถูกต้อง / ไม่ได้ตั้งค่าอย่างถูกต้อง
หากคุณต้องการดูการตั้งค่าเทอร์มินัลปัจจุบันของคุณให้เรียกใช้
stty -a
โดยปกติแล้วแฟล็กอินพุตจะอยู่ที่บรรทัดที่สี่และแฟล็กเอาต์พุตในบรรทัดที่ห้าพร้อมกับ-
ชื่อแฟล็กที่นำหน้าหากไม่ได้ตั้งค่าแฟล็ก ตัวอย่างเช่นผลลัพธ์อาจเป็น
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
บน pseudoterminals และอุปกรณ์ USB TTY อัตรารับส่งข้อมูลไม่เกี่ยวข้อง
หากคุณเขียนสคริปต์ Bash ที่ต้องการอ่านเช่นรหัสผ่านให้พิจารณาสำนวนต่อไปนี้:
#!/bin/bash
trap 'stty sane ; stty '"$(stty -g)" EXIT
stty -echo -echonl -imaxbel -isig -icanon min 1 time 0
EXIT
กับดักจะถูกดำเนินการเมื่อใดก็ตามที่ออกจากเปลือก stty -g
อ่านการตั้งค่าปัจจุบันของอาคารในช่วงเริ่มต้นของสคริปต์เพื่อให้การตั้งค่าปัจจุบันมีการบูรณะเมื่อออกจากสคริปต์โดยอัตโนมัติ คุณสามารถขัดจังหวะสคริปต์ด้วยCtrl+ Cและมันจะทำสิ่งที่ถูกต้อง (ในบางกรณีที่มีสัญญาณฉันพบว่าบางครั้งเทอร์มินัลติดอยู่กับการตั้งค่า raw / noncanonical (ต้องการให้พิมพ์reset
+ Enterสุ่มสี่สุ่มห้าที่เทอร์มินัล) แต่การเรียกใช้stty sane
ก่อนที่จะกู้คืนการตั้งค่าดั้งเดิมที่แท้จริงได้หายไปทุกครั้ง ฉันนั่นคือเหตุผลว่าทำไมมันถึงอยู่ที่นั่นความปลอดภัยที่เพิ่มเข้ามา)
คุณสามารถอ่านบรรทัดอินพุต (ไม่ได้ถูกสะท้อนไปยังเทอร์มินัล) โดยใช้read
bash ในตัวหรือแม้แต่อ่านอักขระทีละอักขระโดยใช้
IFS=$'\0'
input=""
while read -N 1 c ; do
[[ "$c" == "" || "$c" == $'\n' || "$c" == $'\r' ]] && break
input="$input$c"
done
หากคุณไม่ได้ตั้งค่าIFS
เป็น ASCII NUL ในread
ตัวเครื่องจะใช้ตัวคั่นดังนั้นจึงc
จะว่างเปล่า กับดักสำหรับผู้เล่นรุ่นเยาว์