Phát hiện xem stdin là thiết bị đầu cuối hay đường ống?


118

Khi tôi thực thi " python" từ thiết bị đầu cuối không có đối số, nó sẽ hiển thị trình bao tương tác Python.

Khi tôi thực thi " cat | python" từ thiết bị đầu cuối, nó không khởi chạy chế độ tương tác. Bằng cách nào đó, mà không nhận được bất kỳ đầu vào nào, nó đã phát hiện ra rằng nó được kết nối với một đường ống.

Làm cách nào để thực hiện một phát hiện tương tự trong C hoặc C ++ hoặc Qt?


7
Điều bạn muốn không phải là phát hiện xem stdin có phải là một đường ống hay không, nhưng nếu stdin / stdout là một thiết bị đầu cuối.
Juliano

Câu trả lời:


137

Sử dụng 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");

(Trên cửa sổ họ đang bắt đầu bằng dấu gạch dưới: _isatty, _fileno)


13
+1: stdin có thể là một đường dẫn hoặc được chuyển hướng từ một tệp. Tốt hơn nên kiểm tra xem nó tương tác hay không hơn là kiểm tra xem nó không .
John Kugelman

51
Trên POSIX không có io.hisatty()bạn cần phải bao gồm unistd.h.
maxschlepzig

Câu hỏi tiếp theo: làm thế nào để đọc nội dung trong đường ống trong trường hợp stdin không phải là tty? stackoverflow.com/q/16305971/96656
Mathias Bynens,

Lưu ý: Bạn cần kiểm tra stdout (STDOUT_FILENO) nếu bạn muốn xem -output- của bạn có phải là tty hay không, trong trường hợp bạn muốn chặn đầu ra nếu được đưa vào less.
Coroos

71

Tóm lược

Đối với nhiều trường hợp sử dụng, chức năng POSIXisatty() là tất cả những gì nó cần để phát hiện xem stdin có được kết nối với thiết bị đầu cuối hay không. Một ví dụ tối thiểu:

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

Phần sau đây so sánh các phương pháp khác nhau có thể được sử dụng nếu các mức độ tương tác khác nhau phải được kiểm tra.

Phương pháp chi tiết

Có một số phương pháp để phát hiện xem một chương trình có đang chạy tương tác hay không. Bảng sau hiển thị tổng quan:

cmd \ method ctermid open isatty fstat
―――――――――――――――――――――――――――――――――――――――――――――――――― ――――――――――
./test / dev / tty OK CÓ S_ISCHR
./test ≺ test.cc / dev / tty OK KHÔNG S_ISREG
cat test.cc | ./test / dev / tty OK KHÔNG S_ISFIFO
echo ./test | hiện tại / dev / tty FAIL NO S_ISREG

Kết quả là từ hệ thống Ubuntu Linux 11.04 sử dụng chương trình sau:

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

Thiết bị thập phân

Nếu phiên tương tác cần một số khả năng nhất định, bạn có thể mở thiết bị đầu cuối và (tạm thời) đặt các thuộc tính đầu cuối bạn cần thông qua tcsetattr().

Ví dụ Python

Các mã Python rằng quyết định liệu người phiên dịch chạy tương tác sử dụng isatty(). Chức năngPyRun_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);

cuộc gọi 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;

cuộc gọi nào isatty().

Phần kết luận

Có các mức độ tương tác khác nhau. Để kiểm tra xem stdinđược kết nối với một đường ống / tệp hoặc một thiết bị đầu cuối thực isatty()là một phương pháp tự nhiên để thực hiện điều đó.


5

Có lẽ họ đang kiểm tra loại tệp "stdin" với fstat, đại loại như sau:

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

Tất nhiên Python là mã nguồn mở, vì vậy bạn có thể chỉ cần xem những gì chúng làm và biết chắc chắn:

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


4

Trên Windows, bạn có thể sử dụng 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

Gọi stat () hoặc fstat () và xem S_IFIFO có được đặt trong st_mode hay không.


3

Bạn có thể gọi điện stat(0, &result)và kiểm tra !S_ISREG( result.st_mode ). Đó là Posix, không phải C / C ++.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.