【问题标题】:Detect if stdin is a terminal or pipe?检测标准输入是终端还是管道?
【发布时间】:2016-04-27 17:51:56
【问题描述】:

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

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

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

【问题讨论】:

  • 你想要的不是检测 stdin 是否是管道,而是检测 stdin/stdout 是否是终端。

标签: c++ c qt pipe stdin


【解决方案1】:

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

【讨论】:

  • +1:标准输入可以是管道或从文件重定向。最好检查它是否交互的,而不是检查它是否不是
  • 在 POSIX 上没有io.h,对于isatty(),您需要包含unistd.h
  • 后续问题:如果stdin不是tty,如何读出管道内容? stackoverflow.com/q/16305971/96656
  • 注意:如果您想查看您的 -output- 是否为 tty,则需要检查 stdout (STDOUT_FILENO),以防您想在通过管道传输到 less 时抑制输出。跨度>
【解决方案2】:

总结

对于许多用例,POSIX 函数 isatty() 是检测标准输入是否连接到终端所需的全部内容。一个最小的例子:

#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 是 S_ISCHR ./test ≺ test.cc /dev/tty OK NO S_ISREG 猫测试.cc | ./test /dev/tty OK NO S_ISFIFO 回声./测试 |现在 /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>
#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 code that decides whether the interpreter runs interactively 使用 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() 是一种自然的方法 这样做。

【讨论】:

    【解决方案3】:

    可能他们正在使用 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...
      }
      

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

          您可以致电stat(0, &amp;result) 并检查!S_ISREG( result.st_mode )。不过,那是 Posix,而不是 C/C++。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-01-28
            • 1970-01-01
            • 2012-08-30
            • 2016-06-22
            • 2014-07-15
            • 1970-01-01
            • 2010-11-26
            相关资源
            最近更新 更多