C++ 检测stdin是终端还是管道?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1312922/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 19:31:59  来源:igfitidea点击:

Detect if stdin is a terminal or pipe?

c++cqtpipestdin

提问by Mike McQuaid

When I execute "python" from the terminal with no arguments it brings up the Python interactive shell.

当我在python没有参数的情况下从终端执行“ ”时,它会显示 Python 交互式 shell。

When I execute "cat | python" from the terminal it doesn't launch the interactive mode. Somehow, without getting any input, it has detected that it is connected to a pipe.

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

How would I do a similar detection in C or C++ or Qt?

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

回答by RichieHindle

Use isatty:

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

(On windows they're prefixed with underscores: _isatty, _fileno)

(在 Windows 上,它们以下划线为前缀:_isatty_fileno

回答by maxschlepzig

Summary

概括

For many use cases the POSIXfunction isatty()is all what it is needed to detect if stdin is connected to a terminal. A minimal example:

对于许多用例,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;
}

The following section compares different methods that can be used if different degrees of interactivity have to be tested.

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

Methods in Detail

方法详解

There are several methods to detect if a program is running interactively. Following table shows an overview:

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

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

The results are from a Ubuntu Linux 11.04 system using following program:

结果来自使用以下程序的 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;
}

Termimal device

终端设备

If the interactive session needs certain capabilities, you can open the terminal device and (temporarily) set terminal attributes you need via tcsetattr().

如果交互会话需要一定的能力,您可以打开终端设备并(临时)通过 设置您需要的终端属性tcsetattr()

Python Example

Python 示例

The Python code that decides whether the interpreter runs interactivelyuses isatty(). The Function PyRun_AnyFileExFlags()

决定解释器是否以交互方式运行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);

calls Py_FdIsInteractive()

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

which calls isatty().

其中调用isatty().

Conclusion

结论

There are different degrees of interactivity. For checking if stdinis connected to a pipe/file or a real terminal isatty()is a natural method to do that.

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

回答by Eric Melski

Probably they are checking the type of file that "stdin" is with fstat, something like this:

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

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

Of course Python is open source, so you can just look at what they do and know for sure:

当然,Python 是开源的,所以你只要看看他们做了什么就可以确定:

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

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

回答by Glen Knowles

On Windows you can use GetFileType.

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

回答by Marc Mutz - mmutz

You can call stat(0, &result)and check for !S_ISREG( result.st_mode ). That's Posix, not C/C++, though.

你可以打电话stat(0, &result)查一下!S_ISREG( result.st_mode )。不过,那是 Posix,而不是 C/C++。

回答by sigjuice

Call stat() or fstat() and see if S_IFIFO is set in st_mode.

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