Trick an application into thinking its stdout is a

2018-12-31 23:49发布

问题:

I\'m trying to do the opposite of

Detect if stdin is a terminal or pipe?

I\'m running an application that\'s changing its output format because it detects a pipe on stdout, and I want it to think that it\'s an interactive terminal so that I get the same output when redirecting.

I was thinking that wrapping it in an expect script or using a proc_open() in PHP would do it, but it doesn\'t.

Any ideas out there?

回答1:

Aha!

The script command does what we want...

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

Does the trick!



回答2:

Based on Chris\' solution, I came up with the following little helper function:

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

The quirky looking printf is necessary to correctly expand the script\'s arguments in $@ while protecting possibly quoted parts of the command (see example below).

Usage:

faketty <command> <args>

Example:

$ 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


回答3:

The unbuffer script that comes with Expect should handle this ok. If not, the application may be looking at something other than what its output is connected to, eg. what the TERM environment variable is set to.



回答4:

Referring previous answer, on Mac OS X, \"script\" can be used like below...

script -q /dev/null commands...

But, because it may change return code from \"\\n\" to \"\\r\\n\", I needed to run like this.

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

If there are some pipe between these commands, you need to flush stdout. for example:

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


回答5:

Anywhere Python is installed,

echo fakepassword | python -c \'import pty, sys; pty.spawn(sys.argv[1:])\' ssh


回答6:

I don\'t know if it\'s doable from PHP, but if you really need the child process to see a TTY, you can create a 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 */
}

I was actually under the impression that expect itself does creates a PTY, though.



回答7:

Too new to comment on the specific answer, but I thought I\'d followup on the faketty function posted by ingomueller-net above since it recently helped me out.

I found that this was creating a typescript file that I didn\'t want/need so I added /dev/null as the script target file:

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



回答8:

There\'s also a pty program included in the sample code of the book \"Advanced Programming in the UNIX Environment, Second Edition\"!

Here\'s how to compile pty on Mac OS X:

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