[英]How does bash handle control characters?
我正在編寫一個試圖模擬 bash 行為的 shell。 問題是我讀過當 Bash 獲取用戶輸入時,它使用非規范模式並關閉 ECHO 類型,如下所示:
(*conf)->newterm.c_lflag &= ~(ICANON | ECHO);
但是,如果它關閉 ECHO,那么 SIGINT 如何在終端上仍顯示為^C
? 當我嘗試這個時,我得到了字符 �
,它是-1
(如果你嘗試打印它)。
如果我們對 bash 行為給予足夠的關注,除了 ctrl-c AKA SIGINT之外,永遠不會顯示控制字符。 我唯一的理論是 bash 硬編碼^C
並在檢測到SIGINT時將其打印到屏幕上。 我這樣說對嗎? 當然,BASH 或 ZSH 如何在ECHO
和ICANON
關閉的情況下顯示^C
? 我到處尋找,我似乎真的找不到這個問題的答案......
感謝您的時間和解釋!
編輯:這就是我所做的。 首先,我將我的 shell 初始化為:
tcgetattr(STDIN_FILENO, &(*conf)->oldterm);
(*conf)->newterm = (*conf)->oldterm;
(*conf)->newterm.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &(*conf)->newterm); // Apply immediately
然后我按 ctrl-c,我得到一個換行符, ^C
不顯示。 這是因為我禁用了 ECHO,所以 ECHOCTL 也被禁用了。 現在,我想要的是,如果我按ctr-c
,則顯示^C
,但按上箭頭鍵時也沒有^[[A
。 我只是不確定如何配置 termios 來做到這一點。
我相信^C
實際上是由終端設備接口 (tty) 生成的,而不是 Bash 或 Z Shell。 例如,在stty -echoctl
和stty -echo
,不再顯示^C
。 作為另一個例子,一個簡單的 C 程序,在無限循環中具有sleep(1)
s 並且SIGINT
設置為SIG_IGN
(並帶有stty echo
),仍然顯示^C
。
這個例子對我有用,最相關的行是 17、19、20、36 和 37( gcc -Wall
編譯,在 macOS 和 Linux 中,在 Bash 和 Z Shell 中,使用終端 [Apple]、終結者和 GNOME 終端,在 GNU Screen 和/或 tmux 之內和之外,以及使用xterm-256color
和screen-256color
的TERM
s):
1 #include <errno.h>
2 #include <signal.h>
3 #include <stdio.h>
4 #include <termios.h>
5 #include <unistd.h>
6
7 void sh( int _sig )
8 {
9 puts("SIGINT");
10 }
11
12 int main()
13 {
14 struct termios t;
15 void (*s)(int);
16
17 if ( tcgetattr(fileno(stdin),&t) )
18 return(1);
19 t.c_lflag &= ~ECHO;
20 if ( tcsetattr(fileno(stdin),TCSANOW,&t) )
21 return(2);
22 if ( ( s = signal(SIGINT,&sh) ) == SIG_ERR )
23 return(3);
24
25 /**
26 ** While the following `sleep` is running, [ctrl]+c will
27 ** trigger a SIGINT, which will lead to a `sh(SIGINT)` call,
28 ** which will lead to "SIGINT\n" being written to STDOUT,
29 ** and `sleep` will be interrupted, returning sleep seconds
30 ** remaining. "^C" should not be printed to the terminal.
31 **/
32 puts("Tap [ctrl]+c within 60 seconds and expect \"SIGINT\"...");
33 if ( sleep(60) == 0 || errno != EINTR )
34 return(4);
35
36 t.c_lflag |= ECHO;
37 if ( tcsetattr(fileno(stdin),TCSANOW,&t) )
38 return(5);
39 if ( signal(SIGINT,s) == SIG_ERR )
40 return(6);
41
42 /**
43 ** With the default SIGINT handler restored, the following will
44 ** only return if the time fully elapses. And, with the ECHO
45 ** bit restored, "^C" should be printed to the terminal (unless
46 ** such echo'ing was otherwise disabled).
47 **/
48 puts("Tap [ctrl]+c within 60 seconds, expect \"^C\", and expect a 130 return (128+SIGINT)...");
49 sleep(86400);
50
51 return(7);
52 }
PS請記住[ctrl]-c
(通常)觸發SIGINT
,可以在沒有[ctrl]-c
的情況下生成SIGINT
,並且例如kill -INT "$pid"
不應觸發 tty-驅動^C
輸出。
如果 Bash 確實使用了一些非標准輸入模式,它可能只是為了支持行編輯,而不是作為 shell 的核心功能的一部分。 我認為傳統的 unix shell 只會讀取輸入行,執行它們,然后提示。 交互式 shell 會捕獲 sigint,這樣當您按下 ^C 時,shell 就不會讓您注銷,但僅此而已。 您可能想象的很多事情是在 shell 中處理的,實際上並非如此,它們在內核、終端驅動程序、輸入驅動程序等中。
你為什么要模仿 bash,這是為了好玩和學習嗎? 如果你想這樣做,你應該從查看更舊和更簡單的 shell 的源代碼開始。 您當然可以查看舊的 bsd “csh” 代碼,或者可能是原始的 bourne shell 代碼。 這些外殼完成了外殼的本質。 如果您尋找它們,還有更簡單的外殼。 后來的 shell 通過添加行編輯使事情變得復雜,如果您想了解這些,您可以獲取 gnu readline 庫,並將其添加到您自己的應用程序中。 為此,您可以將其添加到您的外殼中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.