Cómo engañar a una aplicación para que piense que su stdout es un terminal, no una tubería

147

Estoy tratando de hacer lo contrario de "¿ Detectar si stdin es un terminal o una tubería? ".

Estoy ejecutando una aplicación que está cambiando su formato de salida porque detecta una tubería en STDOUT, y quiero que piense que es una terminal interactiva para que obtenga la misma salida al redirigir.

Estaba pensando que envolverlo en un expectscript o usar un proc_open()en PHP lo haría, pero no lo hace.

¿Alguna idea por ahí?

el hombre de hojalata
fuente
8
¿ Empty.sf.net ayuda?
ephemient
1
@ephemient: debería haber sido una respuesta. Gran utilidad por cierto ...
neuro
La pregunta habla sobre stdout pero el título menciona stdin. Creo que el título está mal.
Piotr Dobrogost el

Respuestas:

177

¡Ajá!

El scriptcomando hace lo que queremos ...

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

¡Hace el truco!

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
Christopher Oezbek
fuente
1
+1: simplemente tropiezo con el problema con una lib que realiza la inicialización estática. Un cambio reciente en Fedora 12 ha hecho que ese init falle cuando el exe lanzado no estaba en un tty. Tu truco funciona perfectamente. ¡Lo prefiero a unbuffer ya que el script está instalado por defecto!
neuro
scriptincluso está disponible en BusyBox !
dolmen
9
Si desea canalizarlo en algo interactivo, como less -Rdonde va la entrada del terminal less -R, entonces necesita algunos trucos adicionales. Por ejemplo, quería una versión colorida de git status | less. Debe pasar -Ra menos para que respete los colores, y debe usar scriptpara obtener git statusel color de salida. Pero no queremos scriptmantener la propiedad del teclado, queremos que esto vaya a less. Así que utilizo esto ahora y funciona bien: 0<&- script -qfc "git status" /dev/null | less -R . Esos primeros caracteres cierran stdin para este comando.
Aaron McDaid el
2
Nota: Esto no funciona en los casos en que el componente que verifica la interactividad está buscando en la $-variable de shell una "i".
Jay Taylor
1
Esto es increíble. Necesitaba esto para un caso de uso extremadamente raro con una biblioteca Python incrustada dentro de un ejecutable que se ejecuta dentro de Wine. Cuando ejecuté en una terminal, funcionó, pero cuando ejecuté el archivo .desktop que creé, siempre se bloqueaba porque Py_Initializeno veía stdin / stderr adecuado.
Tatsh
60

Basado en la solución de Chris , se me ocurrió la siguiente pequeña función auxiliar:

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

La apariencia peculiar printfes necesaria para expandir correctamente los argumentos de la secuencia de comandos al $@tiempo que protege las partes posiblemente citadas del comando (vea el ejemplo a continuación).

Uso:

faketty <command> <args>

Ejemplo:

$ 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
ingomueller.net
fuente
9
Probablemente quiera usar la --returnopción, si su versión de la scripttiene, para preservar el código de salida del proceso hijo.
jwd
55
Recomiendo cambiar esta función de esta manera: de lo function faketty { script -qfc "$(printf "%q " "$@")" /dev/null; } contrario, se typescriptcreará un archivo llamado cada vez que se ejecute un comando, en muchos casos.
w0rp
1
no parece funcionar en MacOS aunque, entiendo script: illegal option -- f
Alexander Mills
23

El script de unbuffer que viene con Expect debería manejar esto bien. Si no, la aplicación puede estar buscando algo diferente a lo que está conectada su salida, por ejemplo. en qué está configurada la variable de entorno TERM.

Colin Macleod
fuente
17

No sé si es factible desde PHP, pero si realmente necesita el proceso secundario para ver un TTY, puede crear un 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 */
}

Sin expectembargo, en realidad tenía la impresión de que sí crea un PTY.

efímero
fuente
¿Sabes cómo ejecutar nettop como proceso hijo en mac os x? Quiero obtener la salida de nettop en mi aplicación. Intenté usar forkpty pero aún no pude ejecutar nettop con éxito.
Vince Yuan
16

En referencia a la respuesta anterior, en Mac OS X, el "script" se puede usar como a continuación ...

script -q /dev/null commands...

Pero, como puede reemplazar "\ n" con "\ r \ n" en el stdout, también puede necesitar un script como este:

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

Si hay alguna tubería entre estos comandos, debe vaciar stdout. por ejemplo:

script -q /dev/null commands... | ruby -ne 'print "....\n";STDOUT.flush' |  perl -pe 's/\r\n/\n/g'
Tsuneo Yoshioka
fuente
1
Gracias por la sintaxis de OS X, pero, a juzgar por su declaración Perl, parece que quería decir que cambia las instancias de "\ r \ n" a "\ n", no al revés, ¿correcto?
mklement0
8

Demasiado nuevo para comentar sobre la respuesta específica, pero pensé en hacer un seguimiento de la fakettyfunción publicada por ingomueller-net arriba ya que recientemente me ayudó.

Descubrí que esto estaba creando un typescriptarchivo que no quería / necesitaba, así que agregué / dev / null como el archivo de destino del script:

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

A-Ron
fuente
3

Actualización de la respuesta de @ A-Ron a a) funciona en Linux y MacOs b) propaga el código de estado indirectamente (ya que MacOs scriptno lo admite)

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
}

Ejemplos:

$ 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
Jonas Berlin
fuente
2

shellcheck <file> | lessIntenté obtener colores al ejecutar , así que probé las respuestas anteriores, pero producen este extraño efecto donde el texto se desplaza horizontalmente desde donde debería estar:

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.

(Para aquellos que no están familiarizados con shellcheck, se supone que la línea con la advertencia se alinea con el problema).

Para que las respuestas anteriores funcionen con shellcheck, probé una de las opciones de los comentarios:

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

Esto funciona. También agregué --returny usé opciones largas, para hacer que este comando sea un poco menos inescrutable:

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

Trabaja en Bash y Zsh.

Nick ODell
fuente
1

¡También hay un programa pty incluido en el código de muestra del libro "Programación avanzada en el entorno UNIX, segunda edición"!

Aquí se explica cómo compilar pty en Mac OS X:

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 *
franco
fuente
Error realmente extraño también: error rápido: dominio desconocido: codesnippets.joyent.com. Compruebe que este dominio se haya agregado a un servicio.
i336_