Wie erfasst man das Control+D-Signal?

Lesezeit: 7 Minuten

Ich möchte die erfassen Strg+D signal in meinem Programm und schreibe einen Signalhandler dafür. Wie kann ich das machen? Ich arbeite an C und mit a Linux System.

  • Sie könnten mit der Angabe der Plattform und Sprache beginnen!

    – Mitch Weizen

    4. Oktober 2009 um 10:43 Uhr

Benutzeravatar von Pascal Cuoq
Pascal Cuoq

Wie andere schon gesagt haben, zu handhaben Kontrolle+Dbehandeln “Dateiende”.

Kontrolle+D ist eine Kommunikation zwischen dem Benutzer und der Pseudodatei, die Sie als stdin sehen. Es bedeutet nicht speziell “Dateiende”, sondern allgemeiner “die bisher eingegebene Eingabe löschen”. Spülen bedeutet, dass keine read() Der Aufruf von stdin in Ihrem Programm gibt die Länge der Eingabe zurück, 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 noch nicht “return” eingegeben hat. Wenn die Zeile leer ist, dann read() kommt mit Null zurück, was als “Dateiende” interpretiert wird.

Also bei der Nutzung Kontrolle+D um ein Programm zu beenden, funktioniert es nur am Anfang einer Zeile, oder wenn Sie es zweimal tun (erstes Mal zum Spülen, zweites Mal für read() um Null zurückzugeben).

Versuch es:

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

  • Hey, für alle anderen, die diese Frage finden; Bitte hören Sie auf, diese Lösung abzustimmen. Es mag “technisch korrekt” sein, da Control-D kein Signal ist, aber für Programmierer ist es die nutzloseste Antwort auf dieser ganzen Seite.

    – Doug

    19. Dezember 2014 um 3:05 Uhr

  • @Doug Die meisten Programmierer, die mit Control-D umgehen möchten, möchten einfach mit EOF umgehen. Ich sehe in der Frage keinen Hinweis darauf, dass das OP Control-D als Eingabezeichen lesen möchte. Die Antwort könnte vollständiger sein, indem sie eine bestimmte Methode zum Umgang mit Control-D enthält, aber das OP hat keinen Code bereitgestellt, daher ist es schwierig, einen anderen Änderungsvorschlag zu machen, als auf stackoverflow.com/search?q=%5Bc% zu schauen. 5D+evon . Vielleicht passt Ihre Antwort besser zu einer Frage, in der speziell danach gefragt wird, wie Control-D als normales Eingabezeichen behandelt werden soll.

    – Pascal Cuoq

    19. Dezember 2014 um 8:30 Uhr

  • Das Problem hier ist, dass diese Antwort die Frage nicht beantwortet. ‘Einfach mit EOF umgehen’. WIE? Wie verhindern Sie, dass Ihre Anwendung beendet und von stdin gelesen wird? Kannst du stdin irgendwie wieder öffnen? „Mach einfach das, was ich dir eigentlich nicht sagen werde“ ist eine äußerst wenig hilfreiche Antwort.

    – Doug

    19. Dezember 2014 um 8:36 Uhr


  • @Doug Dieser zweite Kommentar macht klarer, was Sie meinen als Ihr erster Kommentar, der eher ein herablassender Aufruf ist, die Antwort so zu bewerten, wie Sie sie ohne Begründung bewerten.

    – Pascal Cuoq

    19. Dezember 2014 um 8:48 Uhr

  • @EnricoMariaDeAngelis Für mich funktioniert es immer noch genau so, wie es war, als ich diese Antwort geschrieben habe: Erstens bewirkt Strg-D, dass „foofoo“ angezeigt wird, und zweitens bringt mich Strg-D zurück zum Terminal. Ich bin noch nie auf ein Terminal gestoßen, das anders funktionierte (unter OS X und Linux), aber vielleicht hält Ihr Terminal aus irgendeinem Grund das erste Strg-D zurück?

    – Pascal Cuoq

    22. Februar 2020 um 17:13 Uhr

Benutzeravatar von Etienne Dechamps
Etienne Dechamps

Strg+D ist nicht a Signal, es ist EOF (End-Of-File). Es schließt die stdin-Pipe. Wenn read(STDIN) 0 zurückgibt, bedeutet dies, dass stdin geschlossen ist, was bedeutet Strg+D getroffen wurde (vorausgesetzt, am anderen Ende der Leitung befindet sich eine Tastatur).

  • Sprechen über Rohr hier ist irreführend. CTRL-D ist nur für Endgeräte relevant, nicht für Pipes, und es ist nur auf der Master-Seite des Pseudo-Terminals relevant oder wenn es vom (echten) Terminal gesendet wird, und nur wenn es drin ist icanon Modus.

    – Stéphane Chazelas

    21. Januar 2014 um 12:59 Uhr

Benutzeravatar von sambowry
sambowry

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 Strg-D) in Strg-C und das VINTR-Zeichen (von Strg-C) in Strg-D. Wenn Sie Strg-D drücken, sendet der Terminaltreiber ein 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 Taste VINTR gedrückt wurde.

  • Dein main Parameterliste benötigt a void. Das ist nicht C++.

    – Jens

    21. September 2012 um 9:01 Uhr

  • Technisch gesehen das ist C++. g++ -std=c++03 test.cpp kompiliert und läuft gut 🙂

    – Ionoklast Brigham

    13. März 2015 um 5:17 Uhr

  • Nur um nett zu dir zu werden: Aber das OP hat nach C gefragt … 🙂

    – Jostikas

    8. Januar 2017 um 14:47 Uhr

  • Was macht der TCSANOW in Ihrem tcsetattr?

    – Fayure

    15. Juni 2021 um 12:27 Uhr

Dougs Benutzeravatar
Doug

Es müssen keine Signale verarbeitet werden.

Sie müssen sicherstellen, dass ISIG nicht auf den Terminal-Flags gesetzt ist, das ist alles.

Hier ist ein vollständiges 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 Kontrolle C und 4 ist Kontrolle D; 26 ist Steuerung z. 113 ist ‘q’. Sehen: http://en.wikipedia.org/wiki/ASCII#ASCII_control_characters für einen vollen Tisch.

Benutzeravatar von Piotr Czapla
Piotr Czapla

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

Ich denke, das ist der einzige Weg, um abzufangen Strg+D soll direkt mit der System-API arbeiten (wie beim Zugriff auf tty)

Benutzeravatar von user175104
Benutzer175104

Sie können poll() verwenden und auf fd #1 auf POLLHUP achten, da die TTY-Schicht ^D in EOF übersetzt.

Benutzeravatar von Georg Gbess
Georg Gbeß

Strg + D Wert in der ASCII-Tabelle ist 4 und ist ein nicht druckbares Zeichen.
Sie können es also mit dem folgenden Code in einem Terminal erfassen. Wenn die getline-Funktion abgerufen wird Strg + D ein Fehler auftritt und der Rückgabewert -1 ist. Sie können den Rückgabewert mit einer Bedingung versehen.

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

1416360cookie-checkWie erfasst man das Control+D-Signal?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy