如何捕获Control + D信号?

52 c unix linux signals

我想在程序中捕获Ctrl+ D信号并为其编写信号处理程序.我怎样才能做到这一点?我正在使用C系统并使用Linux系统.

Pas*_*uoq 78

正如其他人已经说过的那样,处理Control+ D,处理"文件结束".

Control+ D是用户和您看作stdin的伪文件之间的一段通信.它并不意味着具体的"文件结束",但更一般地说是"刷新我到目前为止输入的输入".read()刷新意味着程序中对stdin的任何调用都会返回自上次刷新以来输入的输入长度.如果该行是非空的,则输入可用于您的程序,尽管用户尚未输入"return".如果该行为空,则read()返回零,并将其解释为"文件结束".

因此,当使用Control+ D来结束程序时,它只能在一行的开头工作,或者如果你做两次(第一次刷新,第二次read()返回零).

试试吧:

$ cat
foo
   (type Control-D once)
foofoo (read has returned "foo")
   (type Control-D again)
$
Run Code Online (Sandbox Code Playgroud)

  • 嘿,对于发现这个问题的其他人; 请停止投票这个解决方案.控制-D可能在技术上是正确的-D不是信号,但对于程序员来说,这是整个页面上最无用的答案. (8认同)
  • @Doug 大多数想要处理 control-D 的程序员只想处理 EOF。我在问题中没有看到任何迹象表明OP希望能够将control-D读取为输入字符。通过包含处理 control-D 的特定方法,答案可能会更完整,但 OP 没有提供任何代码,因此除了查看 http://stackoverflow.com/search?q 之外,很难提出任何修改建议=%5Bc%5D+eof 。也许您的答案更适合专门询问如何将 control-D 视为普通输入字符的问题。 (4认同)
  • @Doug第二条评论比第一条评论更清楚地表达了你的意思,这更像是一种居高临下的呼吁,以与你评估答案相同的方式评估答案,但没有提供任何理由。 (3认同)
  • 这里的问题是这个答案没有回答问题。'只处理EOF'。如何?如何防止应用程序被终止并从 stdin 读取?你会以某种方式重新打开标准输入吗?“就做这件事,实际上我不会告诉你怎么做”是一个极其无益的答案。 (2认同)
  • @EnricoMariaDeAngelis 对我来说,它仍然按照我写这个答案时的方式工作:第一个 Ctrl-D 导致显示“foofoo”,第二个 Ctrl-D 带我回到终端。我从未遇到过工作方式不同的终端(在 OS X 和 Linux 上),但也许您的终端由于某种原因保留了第一个 Ctrl-D? (2认同)

Eti*_*mps 25

Ctrl+ D不是信号,它是EOF(文件结束).它关闭了stdin管道.如果read(STDIN)返回0,则意味着stdin关闭,这意味着Ctrl+ D被击中(假设管道的另一端有一个键盘).

  • 在这里谈论_pipe_是误导性的.`CTRL-D`仅与终端设备有关,而不是与管道有关,它仅与伪终端的主端相关或由(实际)终端发送,并且仅在"icanon"模式下. (6认同)

sam*_*wry 14

一个简约的例子:

#include <unistd.h> 
#include <stdio.h> 
#include <termios.h> 
#include <signal.h> 

void sig_hnd(int sig){ (void)sig; printf("(VINTR)"); }

int main(){
  setvbuf(stdout,NULL,_IONBF,0);

  struct termios old_termios, new_termios;
  tcgetattr(0,&old_termios);

  signal( SIGINT, sig_hnd );

  new_termios             = old_termios;
  new_termios.c_cc[VEOF]  = 3; // ^C
  new_termios.c_cc[VINTR] = 4; // ^D
  tcsetattr(0,TCSANOW,&new_termios);

  char line[256]; int len;
  do{
    len=read(0,line,256); line[len]='\0';
    if( len <0 ) printf("(len: %i)",len);
    if( len==0 ) printf("(VEOF)");
    if( len >0 ){
      if( line[len-1] == 10 ) printf("(line:'%.*s')\n",len-1,line);
      if( line[len-1] != 10 ) printf("(partial line:'%s')",line);
    }
  }while( line[0] != 'q' );

  tcsetattr(0,TCSANOW,&old_termios);
}
Run Code Online (Sandbox Code Playgroud)

程序将VEOF字符(从Ctrl-D)更改为Ctrl-C,将VINTR字符(从Ctrl-C)更改为Ctrl-D.如果按Ctrl-D,则终端驱动程序会将SIGINT发送到程序的信号处理程序.

注意:按下VINTR将擦除终端输入缓冲区,因此在按下VINTR键之前无法读取行中键入的字符.

  • 从技术上讲,这_是_C++。`g++ -std=c++03 test.cpp` 编译并运行得很好:) (3认同)
  • 只是为了和你变得可爱:但是OP询问了C ... :) (2认同)

Dou*_*oug 5

无需处理信号。

您需要确保未在终端标志上设置ISIG,仅此而已。

这是使用select避免阻塞stdin的完整示例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <time.h>
#include <sys/select.h>

#define STDIN_FILENO 0

struct termios org_opts;

/** Select to check if stdin has pending input */
int pending_input(void) {
  struct timeval tv;
  fd_set fds;
  tv.tv_sec = 0;
  tv.tv_usec = 0;
  FD_ZERO(&fds);
  FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0
  select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
  return FD_ISSET(STDIN_FILENO, &fds);
}

/** Input terminal mode; save old, setup new */
void setup_terminal(void) {
  struct termios new_opts;
  tcgetattr(STDIN_FILENO, &org_opts);
  memcpy(&new_opts, &org_opts, sizeof(new_opts));
  new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOKE | ISIG | ICRNL);
  tcsetattr(STDIN_FILENO, TCSANOW, &new_opts);
}

/** Shutdown terminal mode */
void reset_terminal(void) {
  tcsetattr(STDIN_FILENO, TCSANOW, &org_opts);
}

/** Return next input or -1 if none */
int next_input(void) {
  if (!pending_input())
    return -1;
  int rtn = fgetc(stdin);
  printf("Found: %d\n", rtn);
  return(rtn);
}

int main()
{
  setup_terminal();

  printf("Press Q to quit...\n");
  for (;;) {
    int key = next_input();
    if (key != -1) {
      if ((key == 113) || (key == 81)) {
        printf("\nNormal exit\n");
        break;
      }
    }
  }

  reset_terminal();
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

doug-2:rust-sys-sterm doug$ cc junk.c
doug-2:rust-sys-sterm doug$ ./a.out
Press Q to quit...
Found: 4
Found: 3
Found: 27
Found: 26
Found: 113

Normal exit
Run Code Online (Sandbox Code Playgroud)

注意 3是控制C,4是控制D;26是控制z。113是“ q”。有关完整表格,请参见:http//en.wikipedia.org/wiki/ASCII#ASCII_control_characters