web-dev-qa-db-ja.com

stdinが端末かパイプかを検出しますか?

引数なしで端末から「python」を実行すると、Python Interactive Shell)が表示されます。

cat | python "ターミナルから対話モードを起動しません。どういうわけか、入力を取得せずに、パイプに接続されていることを検出しました。

C、C++、またはQtで同様の検出を行うにはどうすればよいですか?

107
Mike McQuaid

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

131
RichieHindle

概要

多くのユースケースでは、 [〜#〜] posix [〜#〜] function 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はいS_ISCHR 
 ./ test ≺test.cc/dev/tty OKいいえS_ISREG 
 cat test.cc | ./test/dev/tty OKいいえS_ISFIFO 
 echo ./test |現時点では/ 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コード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()が自然な方法です。

64
maxschlepzig

Stat()またはfstat()を呼び出し、S_IFIFOがst_modeに設定されているかどうかを確認します。

3
sigjuice

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

もちろん、Pythonはオープンソースですので、彼らが何をしているのかを見て、確実に知ることができます:

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

3
Eric Melski

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...
}
2
Glen Knowles

stat(0, &result)を呼び出して、!S_ISREG( result.st_mode )を確認できます。ただし、C/C++ではなく、Posixです。

2