web-dev-qa-db-fra.com

Tromper une application en pensant que sa sortie standard est un terminal, pas un tuyau

J'essaie de faire le contraire de

Détecter si stdin est un terminal ou un tuyau?

J'exécute une application qui change son format de sortie car elle détecte un canal sur stdout, et je veux qu'elle pense que c'est un terminal interactif pour que j'obtienne la même sortie lors de la redirection.

Je pensais que l'envelopper dans un script expect ou utiliser une proc_open() dans PHP le ferait, mais ce n'est pas le cas.

Des idées?

134
Chris

Aha!

La commande script fait ce que nous voulons ...

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

Fait l'affaire!

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
165
Chris

Basé sur solution de Chris , j'ai trouvé la petite fonction d'aide suivante:

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

L'aspect bizarre printf est nécessaire pour développer correctement les arguments du script dans $@ tout en protégeant les parties éventuellement citées de la commande (voir l'exemple ci-dessous).

Utilisation:

faketty <command> <args>

Exemple:

$ 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
55
ingomueller.net

Le script nbuffer fourni avec Expect devrait gérer cela correctement. Sinon, l'application regarde peut-être autre chose que ce à quoi sa sortie est connectée, par exemple. la valeur de la variable d'environnement TERM.

20
Colin Macleod

Je ne sais pas si c'est faisable depuis PHP, mais si vous avez vraiment besoin du processus enfant pour voir un TTY, vous pouvez créer un PTY .

En 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 */
}

J'avais en fait l'impression que expect lui-même crée un PTY, cependant.

16
ephemient

En se référant à la réponse précédente, sur Mac OS X, le "script" peut être utilisé comme ci-dessous ...

script -q /dev/null commands...

Mais, comme il peut changer le code retour de "\ n" en "\ r\n", j'avais besoin de courir comme ça.

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

S'il y a un tuyau entre ces commandes, vous devez vider stdout. par exemple:

script -q /dev/null commands... | Ruby -ne 'print "....\n";STDOUT.flush' |  Perl -pe 's/\r\n/\n/g'
15
Tsuneo Yoshioka

Trop nouveau pour commenter la réponse spécifique, mais je pensais faire un suivi sur la fonction faketty publiée par ingomueller-net ci-dessus depuis qu'elle m'a récemment aidé.

J'ai trouvé que cela créait un fichier TypeScript dont je ne voulais pas/dont j'ai besoin, j'ai donc ajouté/dev/null comme fichier cible du script:

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

7
A-Ron

J'essayais d'obtenir des couleurs lors de l'exécution de shellcheck <file> | less, j'ai donc essayé les réponses ci-dessus, mais elles produisent cet effet bizarre où le texte est décalé horizontalement par rapport à l'endroit où il devrait être:

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.

(Pour ceux qui ne connaissent pas le shellcheck, la ligne avec l'avertissement est censée correspondre à l'endroit où se trouve le problème.)

Pour que les réponses ci-dessus fonctionnent avec shellcheck, j'ai essayé l'une des options des commentaires:

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

Cela marche. J'ai également ajouté --return et utilisé des options longues, pour rendre cette commande un peu moins impénétrable:

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

Fonctionne dans Bash et Zsh.

1
Nick ODell

Il y a aussi un programme pty inclus dans l'exemple de code du livre "Programmation avancée dans l'environnement UNIX, deuxième édition"!

Voici comment compiler pty sur Mac OS X:

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

0
frank