Comment tromper une commande en pensant que sa sortie est dirigée vers un terminal

38

Si une commande change son comportement lorsque sa sortie est dirigée vers un terminal (par exemple, produit une sortie colorée), comment cette sortie peut-elle être redirigée dans un pipeline tout en préservant le comportement modifié? Il doit y avoir un utilitaire pour cela, dont je ne suis pas au courant.

Certaines commandes, par exemple grep --color=always, ont des options pour forcer le comportement, mais la question est de savoir comment contourner des programmes qui reposent uniquement sur le test du descripteur de fichier de sortie.

Si cela compte, mon shell est bashsous Linux.

Amir
la source
Vous devez savoir comment la commande teste son descripteur de fichier de sortie et renvoie en quelque sorte des résultats cohérents avec ceux obtenus par un terminal de sortie.
Andrew Henle

Réponses:

21

Vous pourriez obtenir ce dont vous avez besoin en utilisant unbuffer.

unbufferest un tcl/ expectscript. Regardez la source si vous voulez. Notez également la section CAVEATS dans man.

Notez également qu'il n'exécute pas d'alias tels que:

alias ls='ls --color=auto'

à moins d'ajouter un truc comme l'a noté Stéphane Chazelas:

Si vous faites un alias unbuffer='unbuffer '(notez l'espace de fin), les alias seront développés après unbuffer.

Runium
la source
Remarque sur les alias reconnus - cela ne fonctionnera pas non plus avec d'autres constructions de shell telles que les fonctions intégrées et les fonctions.
Amir
4
Si vous faites un alias unbuffer='unbuffer '(notez l'espace de fin), les alias seront développés après unbuffer.
Stéphane Chazelas
unbufferc'est! sudo apt install expect- Ce n'était pas clair.
loxaxs
22

Une histoire d'outils

Vous n'êtes pas la première personne à vouloir un tel outil. Les gens ont besoin de tels outils depuis 30 ans. Et ils existent aussi depuis presque aussi longtemps.

Le plus ancien outil pour ce genre de chose était le paquet "pty" de Daniel J. Bernstein, décrit par Rich Salz comme un "couteau de Ginsu", que Bernstein a écrit à la fin des années 1990 pour tricher à Nethack (sic!). La version 4 du paquet "pty" a été publiée en 1992 comp.sources.unix(volumes 25 à 127, 127 à 135). Il est toujours localisable sur le World Wide Web. Paul Vixie l'a décrit à l'époque:

Que puis-je dire? Il tranche, il tranche, il lave la vaisselle, il promène le chien. Cela "fonctionne tout simplement", ce qui signifie que si vous suivez les instructions, vous obtiendrez un paquet de travail sans vous arracher les cheveux, grincer des dents ou effectuer d'autres activités de portage standard.

Bernstein a par la suite mis à jour cette information, quelque part à compter du 1999-04-07, avec un paquet "ptyget", qu'il a annoncé:

J'ai mis au point un nouvel pseudo-tty allocator, ptyget. Une version alpha est à ftp://koobera.math.uic.edu/pub/software/ptyget-0.50.tar.gz. Il y a une liste de diffusion ptyget; pour vous inscrire, envoyez un message vide à [email protected]. J'ai conçu l'interface de ptyget à partir de zéro. C'est beaucoup plus modulaire que pty; l'interface de base pty a été divisée en trois parties:

  • ptyget: un petit programme de bas niveau - le seul programme setuid du paquet - qui alloue un nouveau pseudo-terminal et le transmet au programme de votre choix
  • ptyspawn: un autre petit programme qui exécute un processus enfant sous un pseudo-tty, attendant sa sortie et surveillant les arrêts
  • ptyio: un autre programme, légèrement plus volumineux, qui déplace les données dans les deux sens

Le vieux couteau Ginsu ptyest maintenant orthographié ptybandage, ce qui est synonyme de ptyget ptyio -t ptyspawn; pty -d, pour attacher des programmes réseau à des pseudo-ttys, est maintenant orthographié ptyrun, ce qui est synonyme de ptyget ptyio ptyspawn; et nobufest un synonyme de ptyget ptyio -r ptyspawn -23x. J'ai divisé les fonctionnalités de gestion de session dans un package séparé.

Ce paquet séparé était le paquet "sess".

"ptyget" est d'ailleurs remarquable pour illustrer une version très ancienne du système de construction "redo" jamais publié de Berstein, et l'une des rares instances publiées. dependonest un précurseur clair de redo-ifchange.

Usage

ptybandage

ptybandageest ce que les gens veulent généralement dans une session de connexion. Son principal cas d'utilisation consiste à créer des programmes qui sont sensibles au fait que leurs entrées, sorties ou erreurs standard sont connectées à des terminaux fonctionnent de cette manière, même s'ils se trouvent dans des pipelines shell ou que leurs descripteurs de fichier standard sont redirigés vers un fichier.

Il faut une commande à exécuter (qui doit être une commande externe appropriée, bien sûr) et fonctionne de telle manière qu'il estime que son entrée standard, de sortie et d' erreur sont fixés à un terminal, reliant ceux jusqu'aux ptybandage« s entrée standard d'origine, sortie et erreur.

Il traite des nuances de l'exécution sous des shells de contrôle de travaux, en s'assurant qu'un caractère STOP du terminal non seulement s'arrête, ptybandagemais arrête également l'exécution du programme lié au terminal interne.

ptyrun

ptyrunest ce que les gens veulent généralement dans les serveurs de réseau TCP. Son cas d'utilisation principal concerne les environnements d'exécution à distance qui n'ont pas eux-mêmes configuré des terminaux, exécutant des programmes qui ne fonctionnent pas comme ils le souhaitent sans terminal.

Il ne s'attend pas à être exécuté sous un shell de contrôle de travail et si la commande en cours d'exécution reçoit un signal d'arrêt, elle est simplement redémarrée.

Outils disponibles

Dru Nelson publie les versions "pty" 4 et "ptyget".

Paul Jarc publie une version fixe de ptyget, qui tente de traiter les ioctls de périphériques pseudo-terminaux spécifiques au système d’exploitation dans l’original que les systèmes d’exploitation ne fournissent plus.

Le paquet source nosh est fourni avec workalike ptybandangeet des ptyrunscripts, qui utilisent l' execlineoutil de Laurent Bercot et les propres commandes de gestion pseudo-terminal du paquet nosh. Depuis la version 1.23 de nosh, ils sont disponibles pré-emballés dans le paquet nosh-terminal-extras. (Les versions antérieures ne les fournissaient qu'aux personnes ayant construit à partir des sources.)

Quelques exemples d'utilisations

Jurjgen Oskam utilisant ptybandagesous AIX pour alimenter en entrée un document qui précède s'ouvre et lit son terminal de contrôle pour une invite de mot de passe:

$ ptybandage dsmadmc << EOF> uit.txt
joskam
mot de passe
session de requête
processus de requête
quitter
EOF

Andy Bradford utiliseptyrun sous OpenBSD sous daemontools et ucspi-tcp pour rendre le bgplgshprogramme de contrôle de routeur interactif accessible via le réseau tout en laissant croire qu'il parle à un terminal:

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

Lectures complémentaires

JdeBP
la source
Merci pour cette leçon d’histoire, mais les outils que vous proposez ne sont pas disponibles dans une distribution Linux moderne, donc inutiles.
Amir
4
Il y a un tas de liens hypertextes et toute une partie de la réponse consacrée à où les outils les plus certainement sont disponibles.
JdeBP
1
Quelle est votre opinion sur expect?
CMCDragonkai le
20

Vous pouvez utiliser socat pour démarrer votre processus avec un pty connecté et obtenir socat pour connecter l'autre extrémité du pty à un fichier. Quel AFAIU est exactement ce que vous avez demandé:

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

Cette méthode entraînera l' isattyappelé par my-commandà retourner trueet un processus qui s'appuiera uniquement sur celui qui sera trompé pour sortir les codes de contrôle. Notez que certains processus (notamment grep) vérifient également la valeur de la TERMvariable d'environnement. Vous devrez donc peut-être définir une valeur raisonnable, telle que"xterm"

Guss
la source
13

Il existe également une solution intéressante publiée ici sur Super User par KarlC :

Compilez une petite bibliothèque partagée:

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

Ensuite, indiquez à votre commande de charger ce isatty(3)remplacement de manière dynamique:

LD_PRELOAD=./isatty.so mycommand

Cela pourrait ne pas fonctionner pour toutes les commandes, voire en casser certaines de manière inattendue, mais fonctionnerait probablement dans la plupart des cas.

Amir
la source
2
Pour les utilisateurs de MacOS, vous pouvez obtenir le même comportement en utilisantDYLD_INSERT_LIBRARIES=./isatty.so DYLD_FORCE_FLAT_NAMESPACE=y mycommand
Christopher Shroba
12

Que diriez-vous d'utiliser script(1)?

Par exemple:

script -q -c 'ls -G' out_file

Sauvera la lssortie out_fileavec les codes de couleur préservés.

Sagi
la source
Cela ne fonctionne pas ici. Comment les couleurs sont préservées? Existe-t-il un outil que je devrais utiliser pour imprimer out_fileavec ses couleurs?
Kira
1
@Kira pour visualiser des fichiers avec des séquences d'échappement de couleur ANSI, j'utilise less -R. Dans ce cas, cependant, je voulais que la sortie continue dans le pipeline, ce qui a fini par aboutir dans mon terminal. À cattitre d’illustration, c’est quelque chose script -q -c 'ls -G' /dev/null | catqui supprime typescriptcomplètement le fichier, ne laissant que la sortie du programme.
Amir
Pour éviter de créer un fichier, utilisez simplement un tiret ( -) comme scriptfichier de sortie, par exemple:script -q -c 'ls -G' -
Franklin Piat
0

Basé sur la réponse de @ Amir , voici un script qui génère puis inclut la bibliothèque au moment de l'exécution:

#!/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
la source