Come indurre un'applicazione a pensare che il suo stdout sia un terminale, non una pipe


147

Sto cercando di fare il contrario di " Rileva se lo stdin è un terminale o una pipe? ".

Sto eseguendo un'applicazione che sta cambiando il suo formato di output perché rileva una pipe su STDOUT e voglio che pensi che sia un terminale interattivo in modo da ottenere lo stesso output durante il reindirizzamento.

Stavo pensando che avvolgerlo in uno expectscript o usare un proc_open()in PHP lo avrebbe fatto, ma non è così.

Qualche idea là fuori?


8
Fa empty.sf.net aiuto?
effimero

1
@ephemient: avrebbe dovuto essere una risposta. A proposito, util util ...
neuro

La domanda parla di stdout ma il titolo menziona stdin. Penso che il titolo sia sbagliato.
Piotr Dobrogost,

Risposte:


177

Aha!

Il scriptcomando fa quello che vogliamo ...

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

Fa il trucco!

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

1
+1: inciampare sul problema con una lib che esegue l'inizializzazione statica. Un recente cambiamento in Fedora 12 ha fatto fallire l'init quando l'exe in leasing non era in tty. Il tuo trucco funziona perfettamente. L'ho preferito su unbuffer poiché lo script è installato di default!
Neuro,

scriptè persino disponibile in BusyBox !
dolmen,

9
Se vuoi collegarlo a qualcosa di interattivo, come less -R, dove va l'input del terminale less -R, allora hai bisogno di qualche trucco in più. Ad esempio, volevo una versione colorata di git status | less. Devi passare -Ra meno per rispettare i colori e devi usare scriptper ottenere il git statuscolore di output. Ma non vogliamo scriptmantenere la proprietà della tastiera, vogliamo che ci vada less. Quindi io uso questo ora e funziona bene: 0<&- script -qfc "git status" /dev/null | less -R . Quei primi personaggi chiudono lo stdin per questo comandante.
Aaron McDaid,

2
Nota: questo non funziona nei casi in cui il componente che controlla l'interattività sta osservando la $-variabile shell per una "i".
Jay Taylor,

1
Questo è fantastico Ne avevo bisogno per un caso d'uso estremamente raro con una libreria Python incorporata all'interno di un eseguibile che viene eseguito all'interno di Wine. Quando ho eseguito in un terminale ha funzionato, ma quando ho eseguito il file .desktop che ho creato si arrestava sempre perché Py_Initializenon vedeva lo stdin / stderr corretto.
Tatsh,

60

Sulla base della soluzione di Chris , ho pensato alla seguente piccola funzione di supporto:

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

L'aspetto eccentrico printfè necessario per espandere correttamente gli argomenti dello script $@proteggendo al contempo eventuali parti del comando tra virgolette (vedere l'esempio seguente).

Uso:

faketty <command> <args>

Esempio:

$ 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

9
Probabilmente si desidera utilizzare l' --returnopzione, se presente nella propria versione script, per preservare il codice di uscita del processo figlio.
jwd

5
Consiglio di modificare questa funzione in questo modo: in function faketty { script -qfc "$(printf "%q " "$@")" /dev/null; } caso contrario typescript, in molti casi verrà creato un file denominato ogni volta che viene eseguito un comando.
w0rp,

1
non sembra funzionare su MacOS, però script: illegal option -- f
Alexander Mills

23

Lo script non buffer che viene fornito con Expect dovrebbe gestire questo ok. In caso contrario, l'applicazione potrebbe guardare qualcosa di diverso da quello a cui è collegata la sua uscita, ad es. su cosa è impostata la variabile d'ambiente TERM.


17

Non so se sia fattibile da PHP, ma se hai davvero bisogno del processo figlio per vedere un TTY, puoi creare un PTY .

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

In realtà, avevo l'impressione che expectcreasse un PTY.


Sai come eseguire nettop come processo figlio su mac os x? Voglio ottenere l'output di nettop nella mia app. Ho provato a usare forkpty ma non riesco ancora a eseguire nettop con successo.
Vince Yuan,

16

Facendo riferimento alla risposta precedente, su Mac OS X, "script" può essere utilizzato come di seguito ...

script -q /dev/null commands...

Ma poiché potrebbe sostituire "\ n" con "\ r \ n" sullo stdout, potresti anche aver bisogno di uno script come questo:

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

Se tra questi comandi è presente una pipe, è necessario svuotare lo stdout. per esempio:

script -q /dev/null commands... | ruby -ne 'print "....\n";STDOUT.flush' |  perl -pe 's/\r\n/\n/g'

1
Grazie per la sintassi di OS X, ma, a giudicare dall'istruzione Perl, sembra che tu intendessi dire che cambia le istanze di "\ r \ n" in "\ n", non viceversa, giusto?
mklement0

8

Troppo nuovo per commentare la risposta specifica, ma ho pensato di dare seguito alla fakettyfunzione pubblicata da ingomueller-net sopra poiché mi ha aiutato di recente.

Ho scoperto che questo stava creando un typescriptfile che non volevo / non avevo bisogno, quindi ho aggiunto / dev / null come file di destinazione dello script:

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


3

Aggiornamento della risposta di @A-Ron a) lavoro su Linux e MacO b) propagazione indiretta del codice di stato (poiché i MacO scriptnon lo supportano)

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
}

Esempi:

$ 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

2

Stavo cercando di ottenere colori durante l'esecuzione shellcheck <file> | less, quindi ho provato le risposte sopra, ma producono questo bizzarro effetto in cui il testo è sfalsato orizzontalmente da dove dovrebbe essere:

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.

(Per coloro che non hanno familiarità con il shellcheck, la riga con l'avvertimento dovrebbe allinearsi al punto in cui si trova il problema.)

Al fine di rispondere alle risposte sopra per lavorare con shellcheck, ho provato una delle opzioni dai commenti:

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

Questo funziona Ho anche aggiunto --returne usato lunghe opzioni, per rendere questo comando un po 'meno imperscrutabile:

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

Funziona in Bash e Zsh.


1

C'è anche un programma pty incluso nel codice di esempio del libro "Advanced Programming in UNIX Environment, Second Edition"!

Ecco come compilare pty su 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 *

Errore anche molto strano: errore rapido: dominio sconosciuto: codesnippets.joyent.com. Verifica che questo dominio sia stato aggiunto a un servizio.
i336_
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.