stdinがターミナルかパイプかを検出しますか?


118

python引数なしでターミナルから" " を実行すると、Pythonインタラクティブシェルが表示されます。

cat | python端末から" " を実行すると、インタラクティブモードが起動しません。どういうわけか、何も入力せずに、パイプに接続されていることを検出しました。

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


7
stdinがパイプであるかどうかを検出するのではなく、stdin / stdoutがターミナルであるかどうかを確認します。
ジュリアーノ

回答:


137

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


13
+1:stdinはパイプにすることも、ファイルからリダイレクトすることもできます。よりよいそれはかどうかを確認するためにあり、それがあるかどうかを確認するにはよりインタラクティブではありません
John Kugelman、

51
POSIXにはありませんio.hし、ためにisatty()あなたがインクルードする必要がありunistd.h
maxschlepzig 2009

追加質問:stdinがttyでない場合にパイプされたコンテンツを読み取る方法?stackoverflow.com/q/16305971/96656
Mathias Bynens 2013

注:-output-がttyであるかどうかを確認する場合は、stdout(STDOUT_FILENO)を確認する必要がありますless
Coroos 2018年

71

概要

多くの使用例では、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
猫test.cc | ./test / dev / tty OK NO S_ISFIFO
echo ./test | 現在/ dev / ttyは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()することは、それを行うための自然な方法です。


5

おそらく、彼らは "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はオープンソースであるため、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...
}

3

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


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.