Обманите приложение, думая, что его стандартный вывод - это терминал, а не труба
Я пытаюсь сделать противоположность
Определить, является ли 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
Ссылаясь на предыдущий ответ, в 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:
Я пытался получить цвета при беге 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