我想在程序中捕获Ctrl+D信号,并为其编写一个信号处理程序。我该怎么做呢?我正在使用 C 和 Linux 系统。
lymgl2op1#
正如其他人已经说过的,要处理Control + D,请处理"文件结尾"。Control + D是用户和伪文件stdin之间的一种通信方式。它并不特别表示"文件结束",而是更一般地表示"刷新我到目前为止键入的输入"。刷新意味着程序中对stdin的任何read()调用都返回自上次刷新以来键入的输入长度。如果该行非空,虽然用户还没有输入"return",但是输入对你的程序是可用的。2如果行是空的,那么read()返回0,这被解释为"文件结束"。因此,当使用Control + D结束程序时,它只在一行的开头起作用,或者如果你这样做两次(第一次刷新,第二次read()返回零)。试试看:
read()
$ cat foo (type Control-D once) foofoo (read has returned "foo") (type Control-D again) $
bkhjykvo2#
Ctrl+D不是一个信号,它是EOF(文件结束)。它关闭stdin管道。如果read(STDIN)返回0,它意味着stdin关闭,这意味着Ctrl+D被点击了(假设管道的另一端有一个键盘)。
iih3973s3#
一个极简主义的例子:
#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); }
程序将VEOF字符(从Ctrl-D)更改为Ctrl-C,将VINTR字符(从Ctrl-C)更改为Ctrl-D。如果按Ctrl-D,则终端驱动程序将向程序的信号处理程序发送SIGINT。注意:按下VINTR键将清除终端输入缓冲区,因此您无法读取按下VINTR键之前输入的字符。
kq0g1dla4#
不需要处理信号。您需要确保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; }
输出:
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
注:3是对照品C,4是对照品D;26是控制z。113是'q'。参见:http://en.wikipedia.org/wiki/ASCII#ASCII_control_characters获取完整表格。
z3yyvxxp5#
据我所知,Ctrl+D被系统翻译为标准输入的结尾,所以你的应用程序不会收到任何信号。我认为拦截Ctrl+D的唯一方法是直接使用系统API(就像访问TTY一样)
vc9ivgsu6#
可以使用poll()并在fd #1上监视POLLHUP,因为TTY层将^D转换为EOF。
swvgeqrz7#
ascci表中的Ctrl + D值为4,是不可打印的字符。所以你可以用下面的代码在一个终端中捕获它。当getline函数得到Ctrl + D时,一个错误发生,返回值是-1。你可以在返回值上设置一个条件。
#include <stdio.h> #include <stdio.h> #include <stdlib.h> int main(void) { char *buf = malloc(sizeof(char) * 500); size_t size = 500; int nb = getline(&buf, &size, stdin); if (nb == -1) printf("CTRL + D captured\n"); free(buf); return (0); }
whlutmcx8#
您可以使用feof方法检查stdin是否不为,如下所示:
feof
if (feof(stdin)) { // some code exit(0); }
更多详情请参见此/
h9a6wy2h9#
根据手册页,getline()将在读取行失败时返回-1(包括文件结束条件)。这意味着:
getline()
CTRL+D
由于getline()返回类型为ssize_t的值(该值定义为signed integer值),因此您可以通过以下方式构造代码:测试在文件结束条件或无法读取行的情况下返回的-1值。
ssize_t
signed integer
#include <stdio.h> #include <stdlib> int main(void) { char *buffer = NULL; size_t bufsize = 0; ssize_t characters; characters = getline(&buffer, &bufsize, stdin); if (characters == -1) { printf("You entered CTRL+D\n"); } free(buffer); return (0); }
9条答案
按热度按时间lymgl2op1#
正如其他人已经说过的,要处理Control + D,请处理"文件结尾"。
Control + D是用户和伪文件stdin之间的一种通信方式。它并不特别表示"文件结束",而是更一般地表示"刷新我到目前为止键入的输入"。刷新意味着程序中对stdin的任何
read()
调用都返回自上次刷新以来键入的输入长度。如果该行非空,虽然用户还没有输入"return",但是输入对你的程序是可用的。2如果行是空的,那么read()
返回0,这被解释为"文件结束"。因此,当使用Control + D结束程序时,它只在一行的开头起作用,或者如果你这样做两次(第一次刷新,第二次
read()
返回零)。试试看:
bkhjykvo2#
Ctrl+D不是一个信号,它是EOF(文件结束)。它关闭stdin管道。如果read(STDIN)返回0,它意味着stdin关闭,这意味着Ctrl+D被点击了(假设管道的另一端有一个键盘)。
iih3973s3#
一个极简主义的例子:
程序将VEOF字符(从Ctrl-D)更改为Ctrl-C,将VINTR字符(从Ctrl-C)更改为Ctrl-D。如果按Ctrl-D,则终端驱动程序将向程序的信号处理程序发送SIGINT。
注意:按下VINTR键将清除终端输入缓冲区,因此您无法读取按下VINTR键之前输入的字符。
kq0g1dla4#
不需要处理信号。
您需要确保ISIG没有设置在终端标志上,仅此而已。
下面是一个完整的示例,使用select避免在stdin上阻塞:
输出:
注:3是对照品C,4是对照品D;26是控制z。113是'q'。参见:http://en.wikipedia.org/wiki/ASCII#ASCII_control_characters获取完整表格。
z3yyvxxp5#
据我所知,Ctrl+D被系统翻译为标准输入的结尾,所以你的应用程序不会收到任何信号。
我认为拦截Ctrl+D的唯一方法是直接使用系统API(就像访问TTY一样)
vc9ivgsu6#
可以使用poll()并在fd #1上监视POLLHUP,因为TTY层将^D转换为EOF。
swvgeqrz7#
ascci表中的Ctrl + D值为4,是不可打印的字符。
所以你可以用下面的代码在一个终端中捕获它。当getline函数得到Ctrl + D时,一个错误发生,返回值是-1。你可以在返回值上设置一个条件。
whlutmcx8#
您可以使用
feof
方法检查stdin是否不为,如下所示:更多详情请参见此/
h9a6wy2h9#
根据手册页,
getline()
将在读取行失败时返回-1(包括文件结束条件)。这意味着:
getline()
遇到故障时,它将返回-1getline()
读取CTRL+D
时,它将返回-1由于
getline()
返回类型为ssize_t
的值(该值定义为signed integer
值),因此您可以通过以下方式构造代码:测试在文件结束条件或无法读取行的情况下返回的-1值。