如何捕获Control+D信号?

2024-10-10 08:38:00
admin
原创
74
摘要:问题描述:我想在我的程序中捕获Ctrl+D信号并为其编写一个信号处理程序。我该怎么做?我正在使用C 语言并使用Linux系统。解决方案 1:正如其他人已经说过的,要处理Control+ D,就需要处理“文件结束”。Control+D是用户和您视为 stdin 的伪文件之间的一种通信。它并不特指“文件结束”,而...

问题描述:

我想在我的程序中捕获Ctrl+D信号并为其编写一个信号处理程序。我该怎么做?我正在使用C 语言并使用Linux系统。


解决方案 1:

正如其他人已经说过的,要处理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)
$

解决方案 2:

Ctrl+D不是信号,而是 EOF(文件结束符)。它关闭 stdin 管道。如果 read(STDIN) 返回 0,则表示 stdin 已关闭,这意味着+按下(假设管道另一端有键盘)。Ctrl`D`

解决方案 3:

一个简单的例子:

#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]='';
    if( len <0 ) printf("(len: %i)",len);
    if( len==0 ) printf("(VEOF)");
    if( len >0 ){
      if( line[len-1] == 10 ) printf("(line:'%.*s')
",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 键之前行中输入的字符。

解决方案 4:

无需处理信号。

您需要确保终端标志上没有设置 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
", rtn);
  return(rtn);
}

int main()
{
  setup_terminal();

  printf("Press Q to quit...
");
  for (;;) {
    int key = next_input();
    if (key != -1) {
      if ((key == 113) || (key == 81)) {
        printf("
Normal exit
");
        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以查看完整表格。

解决方案 5:

据我所知,Ctrl+D被系统翻译为标准输入的结束,因此您的应用程序不会收到任何信号。

Ctrl我认为拦截+的唯一方法D是直接使用系统 API(如访问 tty)

解决方案 6:

您可以使用 poll() 并观察 fd #1 上的 POLLHUP,因为 TTY 层将 ^D 转换为 EOF。

解决方案 7:

Ctrlascci 表中的+D值为 4,并且是不可打印字符。
因此,您可以使用以下代码在终端中捕获它。当 getline 函数 get 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
");
    free(buf);
    return (0);
}

解决方案 8:

feof您可以使用如下方法检查 stdin 是否不存在:

if (feof(stdin))
{
    // some code
    exit(0);
}

请参阅此处了解更多详情/

解决方案 9:

根据手册页,getline()如果读取行失败(包括文件结尾情况),将返回 -1。如果发生错误,errno将设置为指示原因。

这意味着:

  • getline()遇到失败时,它将返回 -1 并errno使用错误代码的值设置全局变量

  • getline()读取时CTRL+D它将返回 -1 但不会设置errno全局变量。

EINVAL和是 可以设置ENOMEM的两个可能的错误值。errno`getline()`

因此,您可以以这样的方式构造代码,以测试返回的 -1 值是否getline()是文件结束条件或如下错误的结果:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(void)
{
     char *buffer = NULL;
     size_t bufsize = 0;
     ssize_t characters;
     /* initialize errno to 0 (this value will be changed if getline() 
      * encounters an error 
      */
     errno = 0;

     characters = getline(&buffer, &bufsize, stdin);
     if (characters == -1 && errno == 0)
     {
           printf("You entered CTRL+D
");
     }
     free(buffer);
     return (0);
}
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用