Erkennen, ob stdin ein Anschluss oder eine Leitung ist?

118

Wenn ich " python" vom Terminal ohne Argumente ausführe , wird die interaktive Python-Shell aufgerufen.

Wenn ich " cat | python" vom Terminal aus ausführe , wird der interaktive Modus nicht gestartet. Irgendwie hat es ohne Eingabe festgestellt, dass es mit einer Pipe verbunden ist.

Wie würde ich eine ähnliche Erkennung in C oder C ++ oder Qt durchführen?

Mike McQuaid
quelle
7
Sie möchten nicht erkennen, ob stdin eine Pipe ist, sondern ob stdin / stdout ein Terminal ist.
Juliano

Antworten:

137

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

(Unter Windows werden ihnen Unterstriche vorangestellt : _isatty, _fileno)

RichieHindle
quelle
13
+1: stdin kann eine Pipe sein oder aus einer Datei umgeleitet werden. Besser zu überprüfen , ob es ist interaktiv als zu prüfen , ob es nicht .
John Kugelman
51
Unter POSIX gibt es keine io.hund für isatty()Sie müssen enthalten unistd.h.
Maxschlepzig
Folgefrage: Wie kann man den weitergeleiteten Inhalt auslesen, falls stdin kein tty ist? stackoverflow.com/q/16305971/96656
Mathias Bynens
Hinweis: Sie müssen stdout (STDOUT_FILENO) überprüfen, wenn Sie sehen möchten, ob Ihre -output- eine tty ist oder nicht, falls Sie die Ausgabe unterdrücken möchten, wenn Sie in diese weitergeleitet werden less.
Coroos
71

Zusammenfassung

Für viele Anwendungsfälle ist die POSIX- Funktion isatty()alles, was benötigt wird, um festzustellen, ob stdin an ein Terminal angeschlossen ist. Ein minimales Beispiel:

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

Im folgenden Abschnitt werden verschiedene Methoden verglichen, die verwendet werden können, wenn unterschiedliche Interaktivitätsgrade getestet werden müssen.

Methoden im Detail

Es gibt verschiedene Methoden, um festzustellen, ob ein Programm interaktiv ausgeführt wird. Die folgende Tabelle zeigt eine Übersicht:

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

Die Ergebnisse stammen von einem Ubuntu Linux 11.04-System mit folgendem Programm:

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

Endgerät

Wenn für die interaktive Sitzung bestimmte Funktionen erforderlich sind, können Sie das Endgerät öffnen und (vorübergehend) die erforderlichen Terminalattribute festlegen tcsetattr().

Python-Beispiel

Der Python-Code, der entscheidet, ob der Interpreter interaktiv ausgeführt wird, wird verwendet isatty(). Die FunktionPyRun_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);

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

was anruft isatty().

Fazit

Es gibt verschiedene Grade an Interaktivität. Die Überprüfung, ob stdineine Verbindung zu einer Pipe / Datei oder einem realen Terminal besteht, isatty()ist eine natürliche Methode, um dies zu tun.

maxschlepzig
quelle
5

Wahrscheinlich überprüfen sie den Dateityp, den "stdin" mit fstat hat, ungefähr so:

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

Natürlich ist Python Open Source, also können Sie sich einfach ansehen, was sie tun und sicher wissen:

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

Eric Melski
quelle
4

Unter Windows können Sie GetFileType verwenden.

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...
}
Glen Knowles
quelle
3

Rufen Sie stat () oder fstat () auf und prüfen Sie, ob S_IFIFO im st_mode eingestellt ist.

Sigjuice
quelle
3

Sie können anrufen stat(0, &result)und suchen nach !S_ISREG( result.st_mode ). Das ist Posix, nicht C / C ++.

Marc Mutz - mmutz
quelle