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?


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)


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.


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


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

3

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


3

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

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.