wake-up-neo.com

Wie erfasse ich das Control + D-Signal?

Ich möchte das einfangen Ctrl+D signalisiere in meinem Programm und schreibe einen Signalhandler dafür. Wie kann ich das machen? Ich arbeite an C und benutze ein Linux System.

51
Ryan

Wie andere schon gesagt haben, zu handhaben Control+D, Handle "Dateiende" s.

Control+D ist eine Kommunikation zwischen dem Benutzer und der Pseudodatei, die Sie als stdin ansehen. Dies bedeutet nicht spezifisch "Dateiende", sondern allgemeiner "Leeren der bisher eingegebenen Eingabe". Leeren bedeutet, dass jeder read()-Aufruf von stdin in Ihrem Programm mit der Länge der Eingabe zurückgegeben wird, die seit dem letzten Leeren eingegeben wurde. Wenn die Zeile nicht leer ist, wird die Eingabe für Ihr Programm verfügbar, obwohl der Benutzer "return" noch nicht eingegeben hat. Wenn die Zeile leer ist, gibt read() den Wert Null zurück und dies wird als "Dateiende" interpretiert.

Also bei der Verwendung Control+D Um ein Programm zu beenden, funktioniert es nur am Anfang einer Zeile, oder wenn Sie es zweimal tun (beim ersten Mal zum Leeren, beim zweiten Mal für read() zum Zurückgeben von Null).

Versuch es:

$ cat
foo
   (type Control-D once)
foofoo (read has returned "foo")
   (type Control-D again)
$
75
Pascal Cuoq

Ctrl+D ist kein Signal, es ist EOF (Dateiende). Es verschließt die Standardpfeife. Wenn read (STDIN) 0 zurückgibt, bedeutet dies, dass stdin geschlossen ist Ctrl+D wurde getroffen (vorausgesetzt, es gibt eine Tastatur am anderen Ende der Pipe).

24

Ein minimalistisches Beispiel:

#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);
}

Das Programm ändert das VEOF-Zeichen (von Ctrl-D) in Ctrl-C und das VINTR-Zeichen (von Ctrl-C) in Ctrl-D. Wenn Sie Strg-D drücken, sendet der Terminal-Treiber eine SIGINT an den Signalhandler des Programms.

Hinweis: Durch Drücken von VINTR wird der Eingangspuffer des Terminals gelöscht, sodass Sie die in die Zeile eingegebenen Zeichen nicht lesen können, bevor die VINTR-Taste gedrückt wird.

12
sambowry

Soweit ich weiss Ctrl+D wird vom System in das Ende der Standardeingabe übersetzt, sodass Ihre App kein Signal erhält.

Ich denke das ist der einzige Weg abzufangen Ctrl+D ist, direkt mit der System-API zu arbeiten (wie der Zugriff auf tty)

3
Piotr Czapla

Signale müssen nicht verarbeitet werden. 

Sie müssen sicherstellen, dass ISIG nicht auf den Terminalflags gesetzt ist. 

Hier ist ein vollständig enthaltenes Beispiel mit select, um das Blockieren von stdin zu vermeiden:

#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;
}

Ausgabe:

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

NB. 3 ist Steuerung C und 4 ist Steuerung D; 26 ist die Steuerung z. 113 ist 'q' . Eine vollständige Tabelle finden Sie unter: http://en.wikipedia.org/wiki/ASCII#ASCII_control_characters .

2
Doug

Sie können poll () verwenden und nach POLLHUP für fd # 1 suchen, da der TTY-Layer ^ D in EOF übersetzt.

0
user175104