检测标准input是一个terminal或pipe道?

当我从terminal执行“ python ”而没有参数的时候,它会popupPython交互式shell。

当我从terminal执行“ cat | python ”时,它不会启动交互模式。 不知何故,没有得到任何input,它已经检测到它连接到pipe道。

我如何做类似的检测在C或C + +或Qt?

使用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

概要

对于许多用例来说, POSIX函数isatty()就是检测stdin是否连接到terminal所需要的。 一个最小的例子:

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

以下部分比较了不同程度的交互性需要testing时可以使用的不同方法。

方法详细

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

 cmd \ method ctermid open isatty fstat
 -------------------------------------------------- ----------
 ./test / dev / tty是YES S_ISCHR
 ./test≺test.cc / dev / tty OK NO 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; } 

terminal设备

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

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是否连接到pipe道/文件或真正的terminalisatty()是一个自然的方法来做到这一点。

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

可能他们正在检查“stdin”与fstat文件的types,如下所示:

 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(0, &result)并检查!S_ISREG( result.st_mode ) 。 这是Posix,而不是C / C ++。

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