簡體   English   中英

bash 如何處理控制字符?

[英]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 如何在ECHOICANON關閉的情況下顯示^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 -echoctlstty -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-256colorscreen-256colorTERM 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM