当我在终端上执行 "python
" 命令时,没有任何参数的情况下,它会打开 Python 交互式 shell。
当我在终端上执行 "cat | python
" 命令时,它不会启动交互模式。不知何故,在未收到任何输入的情况下,它已经检测到它连接到了一个管道。
如何在 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
io.h
,要使用 isatty()
函数需要包含 unistd.h
头文件。 - maxschlepzig对于许多用例,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 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系统:
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
int main() {
char tty[L_ctermid+1];
ctermid(tty);
printf("ID: %s\n", tty);
int fd = open(tty, O_RDONLY);
if (fd < 0) perror("Could not open terminal");
else {
printf("Opened terminal\n");
struct termios term;
int r = tcgetattr(fd, &term);
if (r < 0) perror("Could not get attributes");
else printf("Got attributes\n");
}
if (isatty(fileno(stdin))) printf("Is a terminal\n");
else printf("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)) printf("S_ISCHR\n");
else if (S_ISFIFO(stats.st_mode)) printf("S_ISFIFO\n");
else if (S_ISREG(stats.st_mode)) printf("S_ISREG\n");
else printf("unknown stat mode\n");
}
return 0;
}
如果交互式会话需要特定的功能,您可以打开终端设备并(暂时)通过tcsetattr()
设置所需的终端属性。
决定解释器是否以交互方式运行的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()
方法。
可能他们正在使用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.
}
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()或fstat()函数,并查看st_mode中是否设置了S_IFIFO。
stat(0, &result)
并检查 !S_ISREG(result.st_mode)
。这是 Posix 标准,而不是 C/C++。