How to write data to existing process's STDIN

2019-01-08 22:32发布

问题:

I'm seeking for ways to write data to the existing process's STDIN from external processes, and found similar question How do you stream data into the STDIN of a program from different local/remote processes in Python? in stackoverlow.

In that thread, @Michael says that we can get file descriptors of existing process in path like below, and permitted to write data into them on Linux.

/proc/$PID/fd/

So, I've created a simple script listed below to test writing data to the script's STDIN (and TTY) from external process.

#!/usr/bin/env python

import os, sys

def get_ttyname():
    for f in sys.stdin, sys.stdout, sys.stderr:
        if f.isatty():
            return os.ttyname(f.fileno())
    return None

if __name__ == "__main__":
    print("Try commands below")

    print("$ echo 'foobar' > {0}".format(get_ttyname()))
    print("$ echo 'foobar' > /proc/{0}/fd/0".format(os.getpid()))

    print("read :: [" + sys.stdin.readline() + "]")

This test script shows paths of STDIN and TTY and then, wait for one to write it's STDIN.

I launched this script and got messages below.

Try commands below
$ echo 'foobar' > /dev/pts/6
$ echo 'foobar' > /proc/3308/fd/0

So, I executed the command echo 'foobar' > /dev/pts/6 and echo 'foobar' > /proc/3308/fd/0 from other terminal. After execution of both commands, message foobar is displayed twice on the terminal the test script is running on, but that's all. The line print("read :: [" + sys.stdin.readline() + "]") was not executed.

Are there any ways to write data from external processes to the existing process's STDIN (or other file descriptors), i.e. invoke execution of the lineprint("read :: [" + sys.stdin.readline() + "]") from other processes?

回答1:

Your code will not work.
/proc/pid/fd/0 is a link to the /dev/pts/6 file.

$ echo 'foobar' > /dev/pts/6
$ echo 'foobar' > /proc/pid/fd/0

Since both the commands write to the terminal. This input goes to terminal and not to the process.

It will work if stdin intially is a pipe.
For example, test.py is :

#!/usr/bin/python

import os, sys
if __name__ == "__main__":
    print("Try commands below")
    print("$ echo 'foobar' > /proc/{0}/fd/0".format(os.getpid()))
    while True:
        print("read :: [" + sys.stdin.readline() + "]")
        pass

Run this as:

$ (while [ 1 ]; do sleep 1; done) | python test.py

Now from another terminal write something to /proc/pid/fd/0 and it will come to test.py



回答2:

I want to leave here an example I found useful. It's a slight modification of the while true trick above that failed intermittently on my machine.

# pipe cat to your long running process
( cat ) | ./your_server &
server_pid=$!
# send an echo to your cat process that will close cat and in my hypothetical case the server too
echo "quit\n" > "/proc/$server_pid/fd/0"

It was helpful to me because for particular reasons I couldn't use mkfifo, which is perfect for this scenario.