Cómo engañar a un comando para que piense que su salida va a una terminal

38

Dado un comando que cambia su comportamiento cuando su salida se dirige a un terminal (por ejemplo, produce una salida coloreada), ¿cómo se puede redirigir esa salida en una tubería mientras se preserva el comportamiento cambiado? Debe haber una utilidad para eso, que no conozco.

Algunos comandos, como grep --color=always, tienen indicadores de opción para forzar el comportamiento, pero la pregunta es cómo solucionar los programas que se basan únicamente en probar su descriptor de archivo de salida.

Si importa, mi shell está bashen Linux.

Amir
fuente
Debe saber cómo el comando prueba su descriptor de archivo de salida y de alguna manera devolver resultados consistentes con los resultados que se verían desde un terminal de salida.
Andrew Henle

Respuestas:

21

Puede obtener lo que necesita usando unbuffer.

unbufferes un tcl/ expectscript. Mira la fuente si quieres. También tenga en cuenta la sección CUEVAS en man.

También tenga en cuenta que no ejecuta alias como:

alias ls='ls --color=auto'

a menos que uno agregue un truco como lo señaló Stéphane Chazelas:

Si hace un alias unbuffer='unbuffer '(observe el espacio final), los alias se expandirán después unbuffer.

Runium
fuente
Nota sobre los alias reconocidos: tampoco funcionará con ninguna otra construcción de shell como funciones y funciones incorporadas.
Amir
44
Si hace un alias unbuffer='unbuffer '(observe el espacio final), los alias se expandirán después unbuffer.
Stéphane Chazelas
unbuffer¡está! sudo apt install expect- Eso no estaba claro.
loxaxs
22

Una historia de conjuntos de herramientas.

No eres la primera persona que quiere esa herramienta. La gente ha querido estas herramientas durante 30 años. Y han existido durante casi tanto tiempo también.

La primera herramienta para este tipo de cosas fue el paquete "pty" de Daniel J. Bernstein, descrito por Rich Salz como un "cuchillo Ginsu", que Bernstein escribió a principios de la década de 1990 para engañar a nethack (¡sic!). La versión 4 del paquete "pty" se publicó en 1992 comp.sources.unix(volumen 25, números 127 a 135). Todavía es localizable en la World Wide Web. Paul Vixie lo describió en ese momento:

¿Qué puedo decir? Rebana, corta en cuadritos, lava platos, pasea al perro. "Simplemente funciona", lo que significa que si sigue las instrucciones obtendrá un paquete de trabajo sin tirar del cabello o rechinar los dientes u otras actividades de portabilidad estándar.

Bernstein más tarde actualizó esto, en algún momento antes del 07/04/1999, con un paquete "ptyget", que anunció:

He creado un nuevo asignador pseudo-tty, ptyget. Una versión alfa está en ftp://koobera.math.uic.edu/pub/software/ptyget-0.50.tar.gz. Hay una lista de correo ptyget; para unirse, envíe un mensaje vacío a [email protected]. Diseñé la interfaz de ptyget desde cero. Es mucho más modular que pty; La interfaz básica de pty ahora se ha dividido en tres partes:

  • ptyget: un pequeño programa de bajo nivel, el único programa setuid en el paquete, que asigna un nuevo pseudo-tty y lo pasa al programa de su elección
  • ptyspawn: otro pequeño programa que ejecuta un proceso secundario bajo un pseudo-tty, esperando a que salga y esperando paradas
  • ptyio: otro programa, solo un poco más grande, que mueve datos de un lado a otro

El viejo cuchillo Ginsu ptyahora se deletrea ptybandage, lo cual es sinónimo de ptyget ptyio -t ptyspawn; pty -d, para adjuntar programas de red a pseudo-ttys, ahora se deletrea ptyrun, lo cual es sinónimo de ptyget ptyio ptyspawn; y nobufes sinónimo de ptyget ptyio -r ptyspawn -23x. He separado las funciones de administración de la sesión en un paquete separado.

Ese paquete separado era el paquete "sess".

"ptyget" es, por cierto, notable por ejemplificar una versión muy temprana y una de las pocas instancias publicadas del propio sistema de compilación "rehacer" nunca publicado por Berstein. dependones un claro precursor redo-ifchange.

Uso

ptybandage

ptybandagees lo que la gente suele querer en una sesión de inicio de sesión. Su caso de uso principal es hacer que los programas que son sensibles a si sus entradas, salidas o errores estándar están conectados a los terminales funcionen de esa manera, incluso si de hecho están en tuberías de shell, o tienen sus descriptores de archivo estándar redirigidos a un archivo.

Se necesita un comando a ejecutar (que tiene que ser un comando externo adecuado, por supuesto) y lo ejecuta de tal manera que se cree que su nivel de entrada, salida y error están asociadas a un terminal, la conexión de aquellos a través de ptybandage's Entrada estándar original, salida y error.

Se ocupa de los matices de ejecutar bajo shells de control de trabajo, asegurando que un carácter STOP de terminal no solo se detenga ptybandagesino que también detenga el programa en ejecución conectado al terminal interno.

ptyrun

ptyrunes lo que la gente suele querer en los servidores de red TCP. Su caso de uso principal es entornos de ejecución remota que no han configurado terminales, ejecutando programas que no funcionan como se desea cuando no hay terminal.

No espera ejecutarse bajo un shell de control de trabajo, y si el comando que se ejecuta recibe una señal de detención, simplemente se reinicia.

Conjuntos de herramientas disponibles

Dru Nelson publica "pty" versión 4 y "ptyget".

Paul Jarc publica una versión fija de ptyget, que intenta lidiar con los ioctls de dispositivos pseudo-terminales específicos del sistema operativo en el original que los sistemas operativos ya no proporcionan.

El paquete fuente nosh viene con workalike ptybandangey ptyrunscripts, que usan la execlineherramienta de Laurent Bercot y los propios comandos de gestión de pseudo-terminal del paquete nosh. A partir de la versión 1.23 de nosh, estos están disponibles preempacados en el paquete nosh-terminal-extras. (Las versiones anteriores solo las suministraban a las personas que construían desde la fuente).

Algunos ejemplos de usos

Jurjgen Oskam utiliza ptybandageen AIX para alimentar la entrada de un documento aquí a un programa que se abre explícitamente y lee su terminal de control para solicitar una contraseña:

$ ptybandage dsmadmc << EOF> uit.txt
joskam
contraseña
sesión de consulta
proceso de consulta
dejar
EOF

Andy Bradford usa ptyrunen OpenBSD bajo daemontools y ucspi-tcp para hacer que el bgplgshprograma de control de enrutador interactivo sea accesible a través de la red mientras hace pensar que está hablando con un terminal:

#! / bin / sh
exec 2> y 1
exec envuidgid rviews tcpserver -vDRHl0 0 23 ptyrun / usr / bin / bgplgsh

Otras lecturas

JdeBP
fuente
Gracias por la lección de historia, pero las herramientas que sugiere no están disponibles en una distribución moderna de Linux, por lo tanto, no son útiles.
Amir
44
Hay un montón de hipervínculos y una sección completa de la respuesta dedicada a dónde las herramientas están definitivamente disponibles.
JdeBP
1
¿Cuál es tu opinión sobre expect?
CMCDragonkai
20

Puede usar socat para comenzar su proceso con una pty conectada y obtener socat para conectar el otro extremo de la pty a un archivo. Qué AFAIU es exactamente lo que preguntaste:

socat EXEC:"my-command",pty GOPEN:mylog.log

Este método hará que isattyllame por my-commandregresar truey un proceso que se basa solo en eso será engañado a los códigos de control de salida. Tenga en cuenta que algunos procesos (en particular grep) también verifican el valor de la TERMvariable de entorno, por lo que es posible que deba establecerlo en algo razonable, como"xterm"

Guss
fuente
13

También hay una buena solución publicada aquí en Super User por KarlC :

Compile una pequeña biblioteca compartida:

echo "int isatty(int fd) { return 1; }" | gcc -O2 -fpic -shared -ldl -o isatty.so -xc -

Luego diga a su comando que cargue esta isatty(3)anulación dinámicamente

LD_PRELOAD=./isatty.so mycommand

Esto podría no funcionar para todos los comandos, incluso puede romper algunos de manera inesperada, pero probablemente funcionaría en la mayoría de los casos.

Amir
fuente
2
Para los usuarios de MacOS, puede obtener el mismo comportamiento utilizandoDYLD_INSERT_LIBRARIES=./isatty.so DYLD_FORCE_FLAT_NAMESPACE=y mycommand
Christopher Shroba
12

¿Qué tal el uso script(1)?

Por ejemplo:

script -q -c 'ls -G' out_file

Guardará la lssalida out_filecon los códigos de color preservados.

sagi
fuente
Esto no funciona aquí. ¿Cómo se conservan los colores? ¿Hay alguna herramienta que deba usar para generar out_filecon sus colores?
Kira
1
@Kira para ver archivos con secuencias de escape de color ANSI, yo uso less -R. Sin embargo, en este caso, quería que la salida continuara en la tubería, que finalmente terminó en mi terminal. Usando catcomo ilustración, fue algo así como script -q -c 'ls -G' /dev/null | cat, que suprime el typescriptarchivo por completo, dejando solo la salida del programa.
Amir
Para evitar crear un archivo, simplemente use un guión ( -) como scriptarchivo de salida, por ejemplo:script -q -c 'ls -G' -
Franklin Piat
0

Basado en la respuesta de @ Amir , aquí hay un script que genera y luego incluye la biblioteca en tiempo de ejecución:

#!/bin/bash
set -euo pipefail

function clean_up {
  trap - EXIT # Restore default handler to avoid recursion
  [[ -e "${isatty_so:-}" ]] && rm "$isatty_so"
}
# shellcheck disable=2154 ## err is referenced but not assigned
trap 'err=$?; clean_up; exit $err' EXIT HUP INT TERM

isatty_so=$(mktemp --tmpdir "$(basename "$0")".XXXXX.isatty.so)
echo "int isatty(int fd) { return 1; }" \
  | gcc -O2 -fpic -shared -ldl -o "$isatty_so" -xc -
# Allow user to SH=/bin/zsh faketty mycommand
"${SH:-$SHELL}" -c 'eval $@' - LD_PRELOAD="$isatty_so" "$@"
Tom Hale
fuente