アプリケーションをだまして、その標準出力をパイプではなくターミナルであると見なす方法


147

stdinがターミナルまたはパイプであるかどうかを検出しますか?」の逆を実行しようとしています

STDOUTでパイプを検出するため、出力形式を変更するアプリケーションを実行しています。リダイレクトするときに同じ出力が得られるように、それがインタラクティブな端末であると考えて欲しいです。

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

そこに何かアイデアはありますか?



1
@ephemient:答えになるはずだった。ちなみに大きなユーティリティ...
ニューロ

質問はstdoutについて話しますが、タイトルはstdinについて言及しています。タイトルが間違っていると思います。
Piotr Dobrogost 2017年

回答:


177

ああ!

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

1
+1:静的初期化を行うライブラリの問題に遭遇しました。Fedora 12の最近の変更により、起動されたexeがttyになかった場合、そのinitが失敗しました。あなたのトリックは完璧に機能します。スクリプトはデフォルトでインストールされるため、バッファリングよりも優先しました!
ニューロ

scriptBusyBoxでも利用可能です!
ドルメン2013年

9
less -R端末入力がのようなインタラクティブなものにパイプしたい場合はless -R、追加のトリックが必要です。たとえば、カラフルなバージョンのが欲しかったgit status | less-R色を尊重するためにlessに渡す必要があり、色を出力scriptするためgit statusに使用する必要があります。ただしscript、キーボードの所有権を保持する必要はありませんless。これをに移動したいと考えています。だから私は今、これを使用し、それがうまく機能します:0<&- script -qfc "git status" /dev/null | less -R 。最初の数文字は、この1つのコマンドの標準入力を閉じます。
アーロン・マクデイド2014年

2
注:これは、対話性をチェックするコンポーネントが$-シェル変数で「i」を探している場合には機能しません。
Jay Taylor、

1
これは素晴らしいです。Wine内で実行される実行可能ファイル内にPythonライブラリが埋め込まれている非常にまれなユースケースでこれが必要でした。端末で実行すると機能しましたが、作成した.desktopファイルを実行すると、Py_Initialize適切なstdin / stderrが表示されなかったため、常にクラッシュしました。
Tatsh 2018

60

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

9
子プロセスの終了コードを保持するために、--returnバージョンにオプションがある場合は、このオプションを使用することをお勧めしscriptます。
jwd

5
この機能を次のように変更することをお勧めしfunction faketty { script -qfc "$(printf "%q " "$@")" /dev/null; } ます。 そうしtypescriptないと、多くの場合、コマンドが実行されるたびにという名前のファイルが作成されます。
w0rp 2017

1
MacOSのカントーで動作するようには思えない、私が取得 script: illegal option -- f
アレクサンダー・ミルズ

23

バッファー解除に付属しているスクリプトを期待 すべきである。この[OK]を扱います。そうでない場合、アプリケーションは、その出力が接続されているもの以外のものを見ている可能性があります。TERM環境変数の設定。


17

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を作成するという印象を受けました。


Mac OS Xで子プロセスとしてネットトップを実行する方法を知っていますか?アプリでネットトップの出力を取得したい。forkptyを使用してみましたが、それでもnettopを正常に実行できませんでした。
Vince Yuan 14

16

以前の回答を参照すると、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'

1
OS X構文に感謝しますが、Perlステートメントから判断すると、「\ r \ n」のインスタンスを「\ n」に変更するという意味でしたが、逆ではありませんか?
mklement0 2013年

8

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

これがtypescript不要なファイルを作成していることがわかったので、スクリプトのターゲットファイルとして/ dev / nullを追加しました。

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


3

@ A-Ronの回答の更新a)LinuxとMacOの両方での作業b)ステータスコードの間接的な伝播(MacO scriptはサポートしていないため)

faketty () {
  # Create a temporary file for storing the status code
  tmp=$(mktemp)

  # Ensure it worked or fail with status 99
  [ "$tmp" ] || return 99

  # Produce a script that runs the command provided to faketty as
  # arguments and stores the status code in the temporary file
  cmd="$(printf '%q ' "$@")"'; echo $? > '$tmp

  # Run the script through /bin/sh with fake tty
  if [ "$(uname)" = "Darwin" ]; then
    # MacOS
    script -Fq /dev/null /bin/sh -c "$cmd"
  else
    script -qfc "/bin/sh -c $(printf "%q " "$cmd")" /dev/null
  fi

  # Ensure that the status code was written to the temporary file or
  # fail with status 99
  [ -s $tmp ] || return 99

  # Collect the status code from the temporary file
  err=$(cat $tmp)

  # Remove the temporary file
  rm -f $tmp

  # Return the status code
  return $err
}

例:

$ faketty false ; echo $?
1

$ faketty echo '$HOME' ; echo $?
$HOME
0

embedded_example () {
  faketty perl -e 'sleep(5); print "Hello  world\n"; exit(3);' > LOGFILE 2>&1 </dev/null &
  pid=$!

  # do something else
  echo 0..
  sleep 2
  echo 2..

  echo wait
  wait $pid
  status=$?
  cat LOGFILE
  echo Exit status: $status
}

$ embedded_example
0..
2..
wait
Hello  world
Exit status: 3

2

実行時に色を取得しようとしていたため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
}

バッシュとZshで動作します。


1

「UNIX環境の高度なプログラミング、第2版」のサンプルコードには、ptyプログラムも含まれています。

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

man 4 pty  #  pty -- pseudo terminal driver

open http://en.wikipedia.org/wiki/Pseudo_terminal

# Advanced Programming in the UNIX Environment, Second Edition
open http://www.apuebook.com

cd ~/Desktop

curl -L -O http://www.apuebook.com/src.tar.gz

tar -xzf src.tar.gz

cd apue.2e

wkdir="${HOME}/Desktop/apue.2e"

sed -E -i "" "s|^WKDIR=.*|WKDIR=${wkdir}|" ~/Desktop/apue.2e/Make.defines.macos

echo '#undef _POSIX_C_SOURCE' >> ~/Desktop/apue.2e/include/apue.h

str='#include   <sys/select.h>'
printf '%s\n' H 1i "$str" . wq | ed -s calld/loop.c

str='
#undef _POSIX_C_SOURCE
#include <sys/types.h>
'
printf '%s\n' H 1i "$str" . wq | ed -s file/devrdev.c

str='
#include <sys/signal.h>
#include <sys/ioctl.h>
'
printf '%s\n' H 1i "$str" . wq | ed -s termios/winch.c

make

~/Desktop/apue.2e/pty/pty ls -ld *

本当に奇妙なエラー:Fastlyエラー:不明なドメイン:codesnippets.joyent.com。このドメインがサービスに追加されていることを確認してください。
i336_
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.