关于c ++:检测stdin是终端还是管道?

Detect if stdin is a terminal or pipe?

当我执行"python时,从终端没有任何论据使交互式Python壳牌升起。

当我执行"cat | python时,从终端不能启动交互模式。有时候,在没有输入的情况下,它检测到它连接到一个管子。

我怎么能在C或C+or QT中进行类似的检测?


使用isatty

1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <io.h>
...    
if (isatty(fileno(stdin)))
    printf("stdin is a terminal
"
);
else
    printf("stdin is a file or a pipe
"
);

(在窗口上,它们前面加下划线:_isatty_fileno)


总结

在许多用例中,posix函数isatty()是检测stdin是否连接到终端所需的全部功能。最简单的例子:

1
2
3
4
5
6
7
8
9
10
11
#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;
}

以下部分比较了在必须测试不同程度的交互时可以使用的不同方法。

具体方法

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

1
2
3
4
5
6
cmd\method             ctermid    open   isatty   fstat
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
./test                 /dev/tty   OK     YES      S_ISCHR
./test ? test.cc       /dev/tty   OK     NO       S_ISREG
cat test.cc | ./test   /dev/tty   OK     NO       S_ISFIFO
echo ./test | at now   /dev/tty   FAIL   NO       S_ISREG

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#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 << '
'
;
  int fd = ::open(tty, O_RDONLY);
  if (fd < 0) perror("Could not open terminal");
  else {
    cout <<"Opened terminal
"
;
    struct termios term;
    int r = tcgetattr(fd, &term);
    if (r < 0) perror("Could not get attributes");
    else cout <<"Got attributes
"
;
  }
  if (isatty(fileno(stdin))) cout <<"Is a terminal
"
;
  else cout <<"Is not a terminal
"
;
  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
"
;
    else if (S_ISFIFO(stats.st_mode)) cout <<"S_ISFIFO
"
;
    else if (S_ISREG(stats.st_mode)) cout <<"S_ISREG
"
;
    else cout <<"unknown stat mode
"
;
  }
  return 0;
}

畸胎装置

如果交互式会话需要某些功能,可以打开终端设备和(临时)设置您需要的终端属性经tcsetattr()

Python示例

决定解释器是否以交互方式运行的python代码使用isatty()。函数PyRun_AnyFileExFlags()

1
2
3
4
5
6
7
8
9
10
/* 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()

1
2
3
4
5
6
7
8
9
10
11
/*
 * 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()是一种自然的方法那样做。


可能他们正在检查"stdin"与fstat之间的文件类型,如下所示:

1
2
3
4
5
6
7
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

希望有帮助,

埃里克梅尔斯基


调用stat()或fstat(),查看是否在st_模式下设置了s_ifo。


在Windows上,可以使用GetFileType。

1
2
3
4
5
6
7
8
9
10
11
12
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...
}

您可以打电话给stat(0, &result)查看!S_ISREG( result.st_mode )。这是POSIX,而不是C/C++。