webentwicklung-frage-antwort-db.com.de

Erkennen, ob stdin eine Klemme oder ein Rohr ist?

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

Wenn ich "cat | python "vom Terminal aus 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?

107
Mike McQuaid

Benutze 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 wird ein Unterstrich vorangestellt: _isatty, _fileno)

131
RichieHindle

Zusammenfassung

Für viele Anwendungsfälle ist die Funktion POSIX isatty() alles, was Sie benötigen, um festzustellen, ob stdin mit verbunden ist ein Terminal. 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;
}

Der folgende Abschnitt vergleicht verschiedene Methoden, die verwendet werden können, wenn verschiedene Grade der Interaktivität 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 NO S_ISFIFO 
 echo ./test | zur Zeit/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 Terminalgerät öffnen und die erforderlichen Terminalattribute (vorübergehend) über tcsetattr() festlegen.

Python-Beispiel

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

ruft Py_FdIsInteractive() auf

/*
 * 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;

welches isatty() aufruft.

Fazit

Es gibt verschiedene Grade an Interaktivität. Um zu überprüfen, ob stdin mit einer Pipe/Datei oder einem realen Terminal verbunden ist, ist isatty() eine natürliche Methode.

64
maxschlepzig

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

3
sigjuice

Wahrscheinlich überprüfen sie den Dateityp, den "stdin" mit fstat hat, in etwa 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, Sie können sich also einfach ansehen, was sie tun, und sicher sein, dass Sie Folgendes wissen:

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

3
Eric Melski

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

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

2