Mendeteksi apakah stdin adalah terminal atau pipa?

118

Ketika saya mengeksekusi " python" dari terminal tanpa argumen, ia menampilkan shell interaktif Python.

Saat saya mengeksekusi " cat | python" dari terminal, mode interaktif tidak diluncurkan. Entah bagaimana, tanpa mendapatkan masukan apa pun, ia telah mendeteksi bahwa ia terhubung ke pipa.

Bagaimana saya melakukan deteksi serupa di C atau C ++ atau Qt?

Mike McQuaid
sumber
7
Yang Anda inginkan bukanlah mendeteksi apakah stdin adalah sebuah pipa, tetapi jika stdin / stdout adalah terminal.
Juliano

Jawaban:

137

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

(Pada jendela mereka diawali dengan garis bawah: _isatty, _fileno)

RichieHindle
sumber
13
+1: stdin dapat berupa pipa atau diarahkan dari file. Lebih baik untuk memeriksa apakah itu adalah interaktif daripada untuk memeriksa apakah itu tidak .
John Kugelman
51
Pada POSIX tidak ada io.hdan untuk isatty()Anda perlu menyertakan unistd.h.
maxschlepzig
Pertanyaan lanjutan: bagaimana cara membaca isi yang sudah disisipkan jika stdin bukan tty? stackoverflow.com/q/16305971/96656
Mathias Bynens
Catatan: Anda perlu memeriksa stdout (STDOUT_FILENO) jika Anda ingin melihat apakah -output- Anda adalah tty atau tidak, jika Anda ingin menyembunyikan output jika disalurkan ke less.
Coroos
71

Ringkasan

Untuk banyak kasus penggunaan, fungsi POSIXisatty() adalah semua yang diperlukan untuk mendeteksi jika stdin terhubung ke terminal. Contoh minimal:

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

Bagian berikut membandingkan metode berbeda yang dapat digunakan jika tingkat interaktivitas yang berbeda harus diuji.

Metode secara Detail

Ada beberapa metode untuk mendeteksi jika suatu program berjalan secara interaktif. Tabel berikut menunjukkan gambaran umum:

cmd \ metode ctermid buka isatty fstat
――――――――――――――――――――――――――――――――――――――――――――――― ――――――――――
./test / dev / tty OK YES S_ISCHR
./test ≺ test.cc / dev / tty OK TIDAK S_ISREG
cat test.cc | ./test / dev / tty OK TIDAK S_ISFIFO
echo ./test | di / dev / tty FAIL NO S_ISREG

Hasil dari sistem Ubuntu Linux 11.04 menggunakan program berikut:

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

Perangkat termimal

Jika sesi interaktif membutuhkan kemampuan tertentu, Anda dapat membuka perangkat terminal dan (sementara) mengatur atribut terminal yang Anda perlukan melalui tcsetattr().

Contoh Python

The kode Python yang memutuskan apakah penafsir berjalan secara interaktif menggunakan isatty(). FungsiPyRun_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);

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

yang memanggil isatty().

Kesimpulan

Ada berbagai tingkat interaktivitas. Untuk memeriksa apakah stdinterhubung ke pipa / file atau terminal nyata isatty()adalah metode alami untuk melakukan itu.

maxschlepzig.dll
sumber
5

Mungkin mereka memeriksa jenis file yang "stdin" dengan fstat, seperti ini:

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

Tentu saja Python adalah open source, jadi Anda bisa melihat apa yang mereka lakukan dan mengetahui dengan pasti:

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

Eric Melski
sumber
4

Di Windows, Anda dapat menggunakan 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...
}
Glen Knowles
sumber
3

Panggil stat () atau fstat () dan lihat apakah S_IFIFO disetel di st_mode.

sigjuice.dll
sumber
3

Anda dapat menelepon stat(0, &result)dan memeriksa !S_ISREG( result.st_mode ). Itu Posix, bukan C / C ++.

Marc Mutz - mmutz
sumber