检测stdin是终端还是管道?

时间:2022-04-19 03:41:10

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中进行类似的检测?

6 个解决方案

#1


115  

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)

#2


60  

Summary

For many use cases the POSIX function 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

The Python code that decides whether the interpreter runs interactively uses 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 stdin is connected to a pipe/file or a real terminal isatty() is a natural method to do that.

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

#3


3  

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

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

#4


3  

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

But why ask us? Python is open source. You can just go 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

Hope that helps,

希望有所帮助,

Eric Melski

Eric Melski

#5


2  

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++。

#6


2  

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

#1


115  

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)

#2


60  

Summary

For many use cases the POSIX function 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

The Python code that decides whether the interpreter runs interactively uses 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 stdin is connected to a pipe/file or a real terminal isatty() is a natural method to do that.

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

#3


3  

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

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

#4


3  

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

But why ask us? Python is open source. You can just go 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

Hope that helps,

希望有所帮助,

Eric Melski

Eric Melski

#5


2  

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++。

#6


2  

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