Hoe een applicatie te misleiden door te denken dat de stdout een terminal is, geen pijp

Ik probeer het tegenovergestelde te doen van “Detecteren of stdin een terminal of pijp is?“.

Ik gebruik een toepassing die het uitvoerformaat verandert omdat het een pijp op STDOUT detecteert, en ik wil dat het denkt dat het een interactieve terminal is, zodat ik dezelfde uitvoer krijg bij het omleiden.

Ik dacht dat het inpakken in een expect-script of het gebruik van een proc_open()in PHP het zou doen, maar dat doet het niet.

Heeft u ideeën?


Antwoord 1, autoriteit 100%

Aha!

De opdracht scriptdoet wat we willen…

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

Zou het lukken!

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

Antwoord 2, autoriteit 39%

Op basis van Chris’ oplossingheb ik de volgende kleine hulpfunctie bedacht:

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

De eigenzinnig uitziende printfis nodig om de argumenten van het script correct uit te breiden in $@terwijl mogelijk geciteerde delen van de opdracht worden beschermd (zie voorbeeld hieronder).

Gebruik:

faketty <command> <args>

Voorbeeld:

$ 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

Antwoord 3, autoriteit 15%

Het unbuffer-script dat wordt geleverd met Verwachtzoudit goed moeten afhandelen. Als dat niet het geval is, kijkt de toepassing mogelijk naar iets anders dan waar de uitvoer op is aangesloten, bijv. waarop de omgevingsvariabele TERM is ingesteld.


Antwoord 4, autoriteit 11%

Verwijzend naar het vorige antwoord, op Mac OS X, kan “script” worden gebruikt zoals hieronder…

script -q /dev/null commands...

Maar omdat het “\n” kan vervangen door “\r\n” op de stdout, heb je misschien ook een script als dit nodig:

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

Als er een pijp tussen deze commando’s zit, moet je stdout doorspoelen. bijvoorbeeld:

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

Antwoord 5, autoriteit 10%

Ik weet niet of het mogelijk is vanuit PHP, maar als je het onderliggende proces echt nodig hebt om een ​​TTY te zien, kun je een 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 */
}

Ik had eigenlijk de indruk dat expectzelf maakt echter een PTY aan.


Antwoord 6, autoriteit 7%

Het antwoord van @A-Ron op . bijwerken
a) werken op zowel Linux & MacOs
b) indirecte statuscode verspreiden (aangezien MacOs scriptdit niet ondersteunt)

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
}

Voorbeelden:

$ 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

Antwoord 7, autoriteit 5%

Te nieuw om commentaar te geven op het specifieke antwoord, maar ik dacht dat ik de functie fakettyzou opvolgen die hierboven door ingomueller-net is gepost, aangezien het me onlangs heeft geholpen.

Ik ontdekte dat dit een typescript-bestand aan het maken was dat ik niet wilde/nodig had, dus voegde ik /dev/null toe als het scriptdoelbestand:

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


Antwoord 8, autoriteit 2%

Er is ook een pty-programma opgenomen in de voorbeeldcode van het boek “Advanced Programming in the UNIX Environment, Second Edition”!

Zo compileert u pty op 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 *

Antwoord 9, autoriteit 2%

Ik probeerde kleuren te krijgen bij het uitvoeren van shellcheck <file> | lessop Linux, dus ik heb de bovenstaande antwoorden geprobeerd, maar ze produceren dit bizarreffect waarbij tekst horizontaal wordt gecompenseerd vanwaar het zou moeten zijn:

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.

(Voor degenen die niet bekend zijn met ShellCheck, wordt de regel met de waarschuwing verondersteld op te staan ​​met het waar het probleem is.)

Voor de bovenstaande antwoorden om met ShellCheck te werken, probeerde ik een van de opties uit de opmerkingen:

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

deze werkt. Ik heb ook --returnen gebruikte lange opties om deze opdracht een beetje minder ondoorgrondelijk te maken:

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

Werkt in Bash en ZSH.

Other episodes