Estoy escribiendo un programa en C++ para Linux. Planeo usar popen() para ejecutar otro programa. Mientras espero que termine ese programa, quiero poder detectar si el usuario presiona teclas en el teclado para saber si quiere continuar procesando sus archivos o si quiere hacer una pausa después de pclose() . Cuál es la mejor manera de lograr esto?
Puede usar un hilo esperando una tecla antes de llamar a popen , llamar a popen , cancelar el hilo y verificar si se presionó alguna tecla antes de llamar a pclose .
Un ejemplo que usa C (es trivial adaptarlo a C ++), estoy omitiendo las comprobaciones de errores por brevedad:
#define _XOPEN_SOURCE #include <stdio.h> #include <pthread.h> #include <unistd.h> void *handler(void *arg) { int *key = arg; char c; while (read(STDIN_FILENO, &c, 1)) { if (*key == 0) { *key = c; } } return NULL; } int main(void) { pthread_t thread; int key = 0; pthread_create(&thread, NULL, handler, &key); // Simulate a long read using sleep 5 // to give you enough time to press a key + enter FILE *cmd = popen("sleep 5; echo 'command finished'", "r"); char str[1024]; while (fgets(str, sizeof str, cmd)) { printf("%s", str); } pthread_cancel(thread); pthread_join(thread, NULL); if (key != 0) { printf("%c was pressed\n", key); } pclose(cmd); return 0; }EDITAR:
Alguien sugirió en los comentarios usar select en lugar de pthread s, no veo mucha ventaja en usar select , pero aquí vamos:
select() permite esperar hasta que un descriptor de archivo (en este caso, stdin ) esté listo, se puede conectar a un tiempo de espera que especifica el intervalo que select() debe bloquear esperando que el descriptor de archivo esté listo, en este caso, ya que queremos read lo que está pendiente en stdin tan pronto como finalice popen , establecemos el tiempo de espera en 0, al final necesitamos limpiar stdin , de lo contrario, terminamos contaminando la terminal con basura si el usuario comienza a escribir pero no acierta enter, o escribe más de un dígito presionando enter.
En mi humilde opinión, los hilos son una alternativa más limpia en este caso. Mismo enfoque usando select :
#define _XOPEN_SOURCE #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/time.h> #include <termios.h> void clear_stdin(void) { int stdin_copy = dup(STDIN_FILENO); tcdrain(stdin_copy); tcflush(stdin_copy, TCIFLUSH); close(stdin_copy); } int keypress(int state) { int key = 0; // state 0 = timeout (nothing to get) if (state == 1) { char c; if (read(STDIN_FILENO, &c, 1)) { key = c; } } clear_stdin(); return key; } int main(void) { fd_set set; FD_ZERO(&set); FD_SET(STDIN_FILENO, &set); FILE *cmd = popen("sleep 5; echo 'command finished'", "r"); char str[1024]; while (fgets(str, sizeof str, cmd)) { printf("%s", str); } struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; int state = select(FD_SETSIZE, &set, NULL, NULL, &timeout); int key = keypress(state); if (key != 0) { printf("%c was pressed\n", key); } pclose(cmd); return 0; }