web-dev-qa-db-ja.com

標準出力がパイプではなく端末であると考えるようにアプリケーションをだます

私は反対のことをしようとしています

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

Stdoutでパイプを検出するために出力形式を変更するアプリケーションを実行していますが、リダイレクト時に同じ出力が得られるように、対話式ターミナルであると考えてほしいです。

expectスクリプトでラッピングするか、PHPでproc_open()を使用するとうまくいくと思っていましたが、そうではありません。

アイデアはありますか?

134
Chris

あぁ!

scriptコマンドは、必要なことを行います...

script --return --quiet -c "[executable string]" /dev/null

トリックをします!

Usage:
 script [options] [file]

Make a TypeScript of a terminal session.

Options:
 -a, --append                  append the output
 -c, --command <command>       run command rather than interactive Shell
 -e, --return                  return exit code of the child process
 -f, --flush                   run flush after each write
     --force                   use output file even when it is a link
 -q, --quiet                   be quiet
 -t[<file>], --timing[=<file>] output timing data to stderr or to FILE
 -h, --help                    display this help
 -V, --version                 display version
165
Chris

クリスの解 に基づいて、次の小さなヘルパー関数を思い付きました。

faketty() {
    script -qfc "$(printf "%q " "$@")" /dev/null
}

風変わりなprintfは、$@内のスクリプトの引数を正しく展開し、コマンドの引用部分を保護するために必要です(以下の例を参照)。

使用法:

faketty <command> <args>

例:

$ python -c "import sys; print sys.stdout.isatty()"
True
$ python -c "import sys; print sys.stdout.isatty()" | cat
False
$ faketty python -c "import sys; print sys.stdout.isatty()" | cat
True
55
ingomueller.net

nbufferExpectshouldに付属するスクリプトは、この問題を処理します。そうでない場合、アプリケーションは、出力が接続されているもの以外の何かを見ている可能性があります。 TERM環境変数の設定値。

20
Colin Macleod

PHPから実行できるかどうかはわかりませんが、TTYを確認するために子プロセスが本当に必要な場合は、 [〜#〜] pty [〜#〜] を作成できます。

Cで:

#include <stdio.h>
#include <stdlib.h>
#include <sysexits.h>
#include <unistd.h>
#include <pty.h>

int main(int argc, char **argv) {
    int master;
    struct winsize win = {
        .ws_col = 80, .ws_row = 24,
        .ws_xpixel = 480, .ws_ypixel = 192,
    };
    pid_t child;

    if (argc < 2) {
        printf("Usage: %s cmd [args...]\n", argv[0]);
        exit(EX_USAGE);
    }

    child = forkpty(&master, NULL, NULL, &win);
    if (child == -1) {
        perror("forkpty failed");
        exit(EX_OSERR);
    }
    if (child == 0) {
        execvp(argv[1], argv + 1);
        perror("exec failed");
        exit(EX_OSERR);
    }

    /* now the child is attached to a real pseudo-TTY instead of a pipe,
     * while the parent can use "master" much like a normal pipe */
}

しかし、実際には expect 自体がPTYを作成するという印象を受けました。

16
ephemient

以前の回答を参照すると、Mac OS Xでは、「スクリプト」は次のように使用できます...

script -q /dev/null commands...

しかし、戻りコードが「\ n」から「\ r\n」に変わる可能性があるため、このように実行する必要がありました。

script -q /dev/null commands... | Perl -pe 's/\r\n/\n/g'

これらのコマンド間にパイプがある場合、stdoutをフラッシュする必要があります。例えば:

script -q /dev/null commands... | Ruby -ne 'print "....\n";STDOUT.flush' |  Perl -pe 's/\r\n/\n/g'
15
Tsuneo Yoshioka

特定の答えについてコメントするにはあまりにも新しいですが、最近助けてくれたので、上記のingomueller-netが投稿したfaketty関数をフォローアップしたいと思いました。

これは、私が欲しくない/必要のないTypeScriptファイルを作成しているので、スクリプトのターゲットファイルとして/ dev/nullを追加しました。

function faketty { script -qfc "$(printf "%q " "$@")" /dev/null ; }

7
A-Ron

shellcheck <file> | lessを実行しているときに色を取得しようとしていたので、上記の答えを試しましたが、テキストが本来あるべき位置から水平方向にオフセットされるという奇妙な効果を生み出します。

In ./all/update.sh line 6:
                          for repo in $(cat repos); do
                                                                  ^-- SC2013: To read lines rather than words, pipe/redirect to a 'while read' loop.

(shellcheckに不慣れな人のために、警告のある行は問題のある場所に並ぶことになっています。)

上記の回答がshellcheckで動作するように、コメントのオプションの1つを試しました。

faketty() {                       
    0</dev/null script -qfc "$(printf "%q " "$@")" /dev/null
}

これは動作します。また、--returnを追加し、長いオプションを使用して、このコマンドをわかりにくくしました。

faketty() {                       
    0</dev/null script --quiet --flush --return --command "$(printf "%q " "$@")" /dev/null
}

BashおよびZshで動作します。

1
Nick ODell

また、「UNIX環境での高度なプログラミング、第2版」という本のサンプルコードに含まれているptyプログラムもあります。

Mac OS Xでptyをコンパイルする方法は次のとおりです。

http://codesnippets.joyent.com/posts/show/8786

0
frank