Terminal

終端上退格鍵的行為

  • January 2, 2018

這是關於退格 ( \b) 字元的行為。我有以下 C 程序:

int main() {
   printf("Hello\b\b");
   sleep(5);
   printf("h\n");
   return 0;
}

我的終端上的輸出是

Helho

隨著游標前進到下一行的第一個位置。

首先,整個內容僅在 5 秒睡眠後列印,因此我推斷從核心到終端的輸出是行緩衝的。所以現在,我的問題是:

  1. 由於\b\b回到兩個空格,到 (second) 的位置l,然後類似於如何l替換為ho應該已替換為\n。為什麼不是?
  2. 如果我刪除 line printf("h\n");,它會列印Hello並返回兩個字元,而不刪除。我從其他答案中得到的這是因為非破壞性退格。為什麼輸入和輸出的這種行為不同?也就是說,如果我在終端中輸入一些東西(即使是同一個程序)並按 Backspace,它會刪除最後一個字元,但不會刪除輸出。為什麼?

如果有幫助,我正在使用 bash 的 xterm 終端上的 Ubuntu 系統上。

首先,整個內容僅在 5 秒睡眠後列印,因此我推斷從核心到終端的輸出是行緩衝的。

不,從您的程序到核心的輸出是行緩衝的。stdio這是whenstdout是終端的預設行為。添加呼叫setbuf(stdout, NULL)以關閉stdout. 見setbuf(3)

  1. 由於\b\b回到兩個空格,到lthen 的位置類似於如何l被替換ho應該已經被替換\n。為什麼不是?

因為換行符只是移動游標(並滾動螢幕),所以它不會作為一個可見的字元列印出來,它會在終端上佔據一個字形的位置。如果我們假設它會取代字形,它會是什麼樣子?

  1. 如果我在同一個程序中輸入一些內容,然後按 Backspace,它會刪除最後一個字元,但不會刪除輸出。為什麼?

好吧,當您鍵入時會發生什麼取決於終端處於什麼模式。粗略地說,它可以處於通常的“熟”模式,終端本身提供基本的行編輯(處理退格);或者在“原始”模式下,所有按鍵都轉到應用程序,由應用程序決定如何處理它們,以及輸出什麼作為響應。熟模式通常伴隨著“本地回顯”,終端(使用者​​本地)在輸入字元時列印出字元。在原始模式下,應用程序通常負責回顯鍵入的字元,以完全控制可見的內容。

有關終端模式的討論,請參見例如這個問題: “原始”和“熟”設備驅動程序之間有什麼區別?

如果您執行 eg cat,終端將處於熟模式(預設)並處理行編輯。例如,點擊x``Backspace``Ctrl-D將導致cat僅讀取空輸入,表示輸入結束。您可以使用strace. 但是,如果您改為執行互動式 Bash shell,它將自行處理退格,並輸出它認為適合執行使用者期望的操作,即擦除一個字元。


strace -etrace=read,write -obash.trace bash這是輸入上述序列後的部分輸出x``Backspace``Ctrl-D

read(0, "x", 1)                         = 1
write(2, "x", 1)                        = 1
read(0, "\177", 1)                      = 1
write(2, "\10\33[K", 4)                 = 4
read(0, "\4", 1)                        = 1

首先, Bash reads 和writes x,將其輸出到終端。然後讀取退格符(八進制的字元程式碼 0177 或十進制的 127),並輸出退格符(八進制 010,十進制 8 ())將游標向後移動並*輸出清除行尾的控制序列*<ESC>*[K. 最後一個\4Ctrl-D,Bash 使用它來退出程序。

(* 在輸入中,Ctrl-H將具有十進製字元程式碼 8。退格與此處相同或 127,再次取決於終端的設置方式。)

相比之下,相同的實驗cat只顯示了一次讀取零字節,即“文件結尾”條件。文件結束可能意味著連接的管道或套接字被關閉,文件的實際結束,或者Ctrl-D在熟模式下從終端接收:

read(0, "", 131072)                     = 0

特別是,cat看不到x,也看不到退格,也看不到Ctrl-D: 它們由終端處理。這可能是核心中的虛擬終端驅動程序;通過串列連接等的實際物理終端;或者像 xterm 這樣的終端仿真器,可以在同一台機器上執行,也可以在 SSH 連接的遠端端執行。這對使用者空間軟體無關緊要。

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