Détecter si stdin est un terminal ou un tuyau?

quand j'exécute " python " à partir du terminal sans argument, Cela fait apparaître le shell interactif Python.

quand j'exécute " cat | python " à partir du terminal il ne lance pas le mode interactif. En quelque sorte, sans aucune saisie, il a détecté qu'il est connecté à un tuyau.

Comment faire une détection similaire en C ou C++ ou Qt?

102
demandé sur Rakete1111 2009-08-21 20:22:14

6 réponses

utiliser isatty :

#include <stdio.h>
#include <io.h>
...    
if (isatty(fileno(stdin)))
    printf( "stdin is a terminal\n" );
else
    printf( "stdin is a file or a pipe\n");

(sur windows ils sont préfixés avec des underscores: _isatty , _fileno )

120
répondu RichieHindle 2009-08-21 16:29:40

résumé

pour de nombreux cas d'utilisation le POSIX fonction isatty() est tout ce qu'il faut pour détecter si stdin est connecté à un terminal. Un exemple minime:

#include <unistd.h>
#include <stdio.h>

int main(int argc, char **argv)
{
  if (isatty(fileno(stdin)))
    puts("stdin is connected to a terminal");
  else
    puts("stdin is NOT connected to a terminal");
  return 0;
}

la section suivante compare différentes méthodes qui peuvent être utilisées si différents degrés d'interactivité doivent être testés.

méthodes en détail

Il existe plusieurs méthodes pour détecter si un programme est exécuté de manière interactive. Le tableau suivant donne un aperçu:

cmd\method             ctermid    open   isatty   fstat
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
./test                 /dev/tty   OK     YES      S_ISCHR
./test ≺ test.cc       /dev/tty   OK     NO       S_ISREG
cat test.cc | ./test   /dev/tty   OK     NO       S_ISFIFO
echo ./test | at now   /dev/tty   FAIL   NO       S_ISREG

les résultats sont d'un système Ubuntu Linux 11.04 en utilisant le programme suivant:

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <iostream>
using namespace std;
int main() {
  char tty[L_ctermid+1] = {0};
  ctermid(tty);
  cout << "ID: " << tty << '\n';
  int fd = ::open(tty, O_RDONLY);
  if (fd < 0) perror("Could not open terminal");
  else {
    cout << "Opened terminal\n";
    struct termios term;
    int r = tcgetattr(fd, &term);
    if (r < 0) perror("Could not get attributes");
    else cout << "Got attributes\n";
  }
  if (isatty(fileno(stdin))) cout << "Is a terminal\n";
  else cout << "Is not a terminal\n";
  struct stat stats;
  int r = fstat(fileno(stdin), &stats);
  if (r < 0) perror("fstat failed");
  else {
    if (S_ISCHR(stats.st_mode)) cout << "S_ISCHR\n";
    else if (S_ISFIFO(stats.st_mode)) cout << "S_ISFIFO\n";
    else if (S_ISREG(stats.st_mode)) cout << "S_ISREG\n";
    else cout << "unknown stat mode\n";
  }
  return 0;
}

dispositif Termimal

si la session interactive nécessite certaines fonctionnalités, vous pouvez ouvrir le terminal et (temporairement) définissez les attributs que vous devez via tcsetattr() .

Exemple Python

le code Python qui décide si l'interpréteur fonctionne de manière interactive utilise isatty() . La Fonction PyRun_AnyFileExFlags()

/* Parse input from a file and execute it */

int
PyRun_AnyFileExFlags(FILE *fp, const char *filename, int closeit,
                     PyCompilerFlags *flags)
{
    if (filename == NULL)
        filename = "???";
    if (Py_FdIsInteractive(fp, filename)) {
        int err = PyRun_InteractiveLoopFlags(fp, filename, flags);

appels Py_FdIsInteractive()

/*
 * The file descriptor fd is considered ``interactive'' if either
 *   a) isatty(fd) is TRUE, or
 *   b) the -i flag was given, and the filename associated with
 *      the descriptor is NULL or "<stdin>" or "???".
 */
int
Py_FdIsInteractive(FILE *fp, const char *filename)
{
    if (isatty((int)fileno(fp)))
        return 1;

qui appelle isatty() .

Conclusion

il existe différents degrés d'interactivité. Pour vérifier si stdin est connecter à un tuyau/fichier ou un vrai terminal isatty() est une méthode naturelle pour le faire.

62
répondu maxschlepzig 2014-08-14 21:03:32

appeler stat() ou fstat() et voir si S_IFIFO est défini dans st_mode.

3
répondu sigjuice 2009-08-21 16:24:30

probablement ils vérifient le type de fichier que "stdin" est avec fstat, quelque chose comme ceci:

struct stat stats;
fstat(0, &stats);
if (S_ISCHR(stats.st_mode)) {
    // Looks like a tty, so we're in interactive mode.
} else if (S_ISFIFO(stats.st_mode)) {
    // Looks like a pipe, so we're in non-interactive mode.
}

mais pourquoi nous demander? Python est open source. Vous pouvez simplement aller regarder ce qu'ils font, et je sais:

http://www.python.org/ftp/python/2.6.2/Python-2.6.2.tar.bz2

Espère que ça aide,

Eric Melski

3
répondu Eric Melski 2009-08-21 16:28:49

vous pouvez appeler stat(0, &result) et vérifier !S_ISREG( result.st_mode ) . C'est Posix, pas de C/C++, cependant.

2
répondu Marc Mutz - mmutz 2011-09-29 13:56:40

sous Windows, vous pouvez utiliser GetFileType.

HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
DWORD type = GetFileType(hIn);
switch (type) {
case FILE_TYPE_CHAR: 
    // it's from a character device, almost certainly the console
case FILE_TYPE_DISK:
    // redirected from a file
case FILE_TYPE_PIPE:
    // piped from another program, a la "echo hello | myprog"
case FILE_TYPE_UNKNOWN:
    // this shouldn't be happening...
}
2
répondu Glen Knowles 2016-12-27 23:42:40