Command-Line

為什麼 Enter 鍵不發送 EOL?

  • April 6, 2019

Unix / Linux EOL 是 LF、換行、ASCII 10、轉義序列\n

這是一個 Python 片段,可以準確地獲得一個按鍵:

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

當我按下Enter我的鍵盤來響應這個片段時,它給出了\r,輸入,ASCII 13。

Windows上,Enter發送CR LF == 13 10. *nix 不是 Windows;為什麼Enter給13而不是10?

雖然Thomas Dickey 的回答非常正確,但 Stéphane Chazelas 在對 Dickey 回答的評論中正確地提到了轉換不是一成不變的;它是線路紀律的一部分。

事實上,翻譯是完全可程式的。

man 3 termios手冊頁基本上包含所有相關資訊。(該連結指向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 或單一 Unix 規範系統中都可用。)
  • ONOCR: 跳過(不輸出)第一列的 CR。
  • ONLRET:跳過(不輸出)CR。

例如,您可以避免依賴tty模組。“makeraw”操作只是清除一組標誌(並設置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

儘管出於兼容性考慮,您可能希望首先檢查 termios 模組中是否存在所有這些常量(如果您在非 POSIX 系統上執行)。您還可以使用new_settings[6][termios.VMIN]andnew_settings[6][termios.VTIME]設置如果沒有待處理的數據,讀取是否會阻塞,以及多長時間(以十進制整數為單位)。(通常VMIN設置為 0,VTIME如果讀取應立即返回,則設置為 0,或者設置為正數(十分之一秒)讀取最多應等待多長時間。)

如您所見,上述(以及一般的“makeraw”)禁用了輸入的所有翻譯,這解釋了 cat 看到的行為:

   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行禁用所有輸出處理,不管其他輸出標誌說什麼。您可以省略它以保持輸出處理完整。即使在原始模式下,這也可以保持輸出“正常”。(它不影響輸入是否自動回顯;由 中的ECHOcflag控制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

在偽終端和 USB TTY 設備上,波特率無關緊要。

如果您編寫希望讀取密碼等 Bash 腳本,請考慮以下習慣用法:

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

每當 shell 退出時,EXIT就會執行陷阱。在stty -g腳本開始時讀取終端的目前設置,因此腳本退出時會自動恢復目前設置。你甚至可以用Ctrl+中斷腳本C,它會做正確的事情。(在某些帶有信號的極端情況下,我發現終端有時會被原始/非規範設置卡住(需要一個人在終端上盲目地鍵入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它將為空。年輕球員的陷阱。

本質上是“因為自從手動打字機以來就是這樣做的”。真的。

手動打字機有一個供紙的托架,它在您打字時向前移動(載入彈簧),並且有一個槓桿或鑰匙可以釋放托架,讓彈簧將托架返回到左邊距。

隨著電子數據輸入(電傳打字機等)的引入,他們將其發揚光大。所以Enter很多終端上的鑰匙都會被貼上標籤Return

將托架返回到左邊距後發生換行(在手動過程中)。再次,電子設備模仿手動設備,進行單獨line-feed操作。

這兩個操作都是編碼的(允許電傳打字機不僅僅是創建紙張類型的獨立設備),所以我們有CR(輸入)和LF(換行)。這張來自ASR 33 Teletype Information的圖像顯示了鍵盤,Return右側和Line-Feed左側。在右邊,這是主要的關鍵:

在此處輸入圖像描述

Unix 是後來出現的。它的開發者喜歡縮短東西(看看所有的縮寫,甚至creat是“create”)。面對可能由兩部分組成的過程,他們認為換行只有在它們之前有輸入時才有意義。因此,他們從files中刪除了顯式輸入,並翻譯了終端的Return鍵以發送相應的換行符。為了避免混淆,他們將換行稱為“換行符”。

在終端上寫文本時,Unix 會朝另一個方向轉換:換行變成輸入/換行。

(即“通常”:所謂的“熟模式”,與不進行翻譯的“原始”模式形成對比)。

概括:

  • 輸入/換行是序列 13 10
  • 設備發送 13(因為在您的術語中是“永遠”)
  • 類 Unix 系統將其更改為 13 10
  • 其他系統不一定只儲存 10(Windows 在很大程度上只接受 10 或 13 10,這取決於兼容性的重要性)。

引用自:https://unix.stackexchange.com/questions/264920