检测stdin是终端还是管道?


118

当我python从终端不带任何参数执行“ ”时,它将弹出Python交互式shell。

当我cat | python从终端执行“ ”时,它不会启动交互模式。不知何故,它没有得到任何输入,就检测到它已连接到管道。

如何在C或C ++或Qt中进行类似的检测?


7
您要检测的不是stdin是否是管道,而是stdin / stdout是否是终端。
朱利诺

Answers:


137

用途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");

(在Windows上他们前面带下划线:_isatty_fileno


13
+1:stdin可以是管道,也可以从文件重定向。更好地检查,如果它互动的,而不是检查它是否没有
约翰·库格曼

51
在POSIX上没有io.hisatty()您需要包含unistd.h
maxschlepzig 2011年

后续问题:如果stdin不是tty,如何读出管道内容?stackoverflow.com/q/16305971/96656
Mathias Bynens

注意:如果要查看-output-是否是tty,则需要检查stdout(STDOUT_FILENO),以防在通过管道传递时抑制输出less
Coroos

71

摘要

在许多使用情况下,仅需POSIX函数isatty()即可检测stdin是否已连接到终端。一个最小的例子:

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

下一节比较了必须测试不同程度的交互性时可以使用的不同方法。

详细方法

有几种方法可以检测程序是否以交互方式运行。下表显示了概述:

cmd \ method ctermid打开isatty fstat
―――――――――――――――――――――――――――――――――――――――――――――――――――― ――――――――――
./test / dev / tty确定是S_ISCHR
./test≺test.cc / dev / tty OK否S_ISREG
cat test.cc | ./test / dev / tty OK否S_ISFIFO
回声./test | 现在/ dev / tty失败S_ISREG

结果来自使用以下程序的Ubuntu Linux 11.04系统:

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

终端设备

如果交互式会话需要某些功能,则可以打开终端设备,并通过(临时)设置所需的终端属性tcsetattr()

Python范例

决定解释器是否以交互方式运行Python代码使用isatty()。功能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);

来电 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;

哪个打电话isatty()

结论

有不同程度的交互性。检查stdin连接到管道/文件或真实终端isatty()的自然方法。


5

可能他们正在使用fstat检查“ stdin”文件的类型,如下所示:

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

当然,Python是开源的,因此您可以看看它们的作用并确定知道:

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


4

在Windows上,您可以使用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

调用stat()或fstat()并查看是否在st_mode中设置了S_IFIFO。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.