Rileva se stdin è un terminale o un tubo?


118

Quando eseguo "python " dal terminale senza argomenti, viene visualizzata la shell interattiva di Python.

Quando eseguo " cat | python" dal terminale non avvia la modalità interattiva. In qualche modo, senza ottenere alcun input, ha rilevato che è collegato a un tubo.

Come farei un rilevamento simile in C o C ++ o Qt?


7
Quello che vuoi non è rilevare se stdin è una pipe, ma se stdin / stdout è un terminale.
Juliano

Risposte:


137

Usa 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");

(Su Windows che stanno prefisso di sottolineatura: _isatty, _fileno)


13
+1: stdin può essere una pipe o reindirizzato da un file. Meglio controllare se è interattivo che per verificare se è non è .
John Kugelman

51
Su POSIX non c'è io.he per isatty()te è necessario includere unistd.h.
maxschlepzig

Domanda di follow-up: come leggere i contenuti convogliati nel caso in cui stdin non sia un tty? stackoverflow.com/q/16305971/96656
Mathias Bynens

Nota: devi controllare stdout (STDOUT_FILENO) se vuoi vedere se la tua -output- è una tty o meno, nel caso in cui desideri sopprimere l'output se reindirizzato in less.
Coroos

71

Sommario

Per molti casi d'uso la funzione POSIXisatty() è tutto ciò che serve per rilevare se stdin è connesso a un terminale. Un esempio minimo:

#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 sezione seguente mette a confronto diversi metodi che possono essere utilizzati se devono essere testati diversi gradi di interattività.

Metodi in dettaglio

Esistono diversi metodi per rilevare se un programma è in esecuzione in modo interattivo. La tabella seguente mostra una panoramica:

cmd \ method ctermid apre isatty fstat
-------------------------------------------------- ----------
./test / dev / tty OK SÌ S_ISCHR
./test ≺ test.cc / dev / tty OK NO S_ISREG
cat test.cc | ./test / dev / tty OK NO S_ISFIFO
echo ./test | adesso / dev / tty FAIL NO S_ISREG

I risultati provengono da un sistema Ubuntu Linux 11.04 utilizzando il seguente programma:

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

Dispositivo termico

Se la sessione interattiva richiede determinate capacità, è possibile aprire il dispositivo terminale e (temporaneamente) impostare gli attributi del terminale necessari tramite tcsetattr().

Esempio di Python

Il codice Python che decide se l'interprete viene eseguito in modo interattivo utilizza isatty(). La funzionePyRun_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);

chiamate 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;

che chiama isatty().

Conclusione

Esistono diversi gradi di interattività. Per controllare se stdinè connesso a una pipe / file o un vero terminale isatty()è un metodo naturale per farlo.


5

Probabilmente stanno controllando il tipo di file che "stdin" è con fstat, qualcosa del genere:

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.
}

Ovviamente Python è open source, quindi puoi semplicemente guardare cosa fanno e sapere con certezza:

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


4

Su Windows puoi usare 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...
}

3

Chiama stat () o fstat () e controlla se S_IFIFO è impostato in st_mode.


3

Puoi chiamare stat(0, &result)e verificare !S_ISREG( result.st_mode ). Questo è Posix, non C / C ++, però.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.