Обманите приложение, думая, что его стандартный вывод - это терминал, а не труба

Я пытаюсь сделать противоположность

Определить, является ли stdin терминалом или каналом?

Я запускаю приложение, которое меняет свой формат вывода, потому что оно обнаруживает канал в stdout, и я хочу, чтобы оно считало, что это интерактивный терминал, поэтому я получаю тот же вывод при перенаправлении.

Я думал, что завернуть его в expect сценарий или использование proc_open() в PHP сделал бы это, но это не так.

Есть идеи?

11 ответов

Ага!

script команда делает то, что мы хотим...

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

Делает трюк!

Основываясь на решении Криса, я предложил следующую маленькую вспомогательную функцию:

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

Сценарий unbuffer, который поставляется с Expect,должен с этим справиться. Если нет, приложение может искать что-то, кроме того, к чему подключен его выход, например. для чего установлена ​​переменная окружения TERM.

Ссылаясь на предыдущий ответ, в Mac OS X можно использовать "скрипт", как показано ниже...

script -q /dev/null commands...

Но, поскольку он может изменить код возврата с "\n" на "\r\n", мне нужно было работать так.

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

Если между этими командами есть какой-то канал, вам нужно сбросить стандартный вывод. например:

script -q /dev/null commands... | ruby -ne 'print "....\n";STDOUT.flush' |  perl -pe 's/\r\n/\n/g'

Я не знаю, выполнимо ли это из PHP, но если вам действительно нужен дочерний процесс для просмотра TTY, вы можете создать PTY.

В С:

#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, хотя.

Обновление ответа @A-Ron на: а) работу как на Linux, так и на MacO; б) косвенное распространение кода состояния (поскольку MacOs 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

Везде, где установлен Python,

echo fakepassword | python -c 'import pty, sys; pty.spawn(sys.argv[1:])' ssh

Слишком новый, чтобы комментировать конкретный ответ, но я подумал, что я буду следить за faketty Функция размещена на ingomueller-net выше, так как она недавно помогла мне.

Я обнаружил, что это создает typescript файл, который мне не нужен / нужен, поэтому я добавил /dev/null в качестве целевого файла скрипта:

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

В пример кода из книги "Расширенное программирование в среде UNIX, второе издание" включена pty-программа!

Вот как скомпилировать pty на Mac OS X:

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

Я пытался получить цвета при беге 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, я попробовал один из вариантов из комментариев:

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

Это работает. Я также добавил--return и использовал длинные параметры, чтобы сделать эту команду менее загадочной:

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

Работает в Баш и ЗШ.

На всякий случай, если вы ищете цветовую подсветкуAnsibleвывод сteeвы также мое использование

      export ANSIBLE_FORCE_COLOR=True
Другие вопросы по тегам