How to write data to existing process's STDIN from external process?
Asked Answered
L

2

33

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?

Lubricant answered 21/3, 2011 at 5:46 Comment(1)
Would a named pipe (see man mkfifo) do what you want?Felicidadfelicie
C
26

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

Cyler answered 21/3, 2011 at 16:43 Comment(4)
Script and command you suggested works correctly. So, once I created the process from terminal without pipe like $ python test.py, there are no way to execute print("read :: [" + sys.stdin.readline() + "]") from external process?Lubricant
@mooz: AFAIK there is no way to do that.Cyler
@Lubricant ... Your 'client-side' can use the command source fifo-file-name. Perhaps you could do that with a start-up script like a special .bashrc ???Floruit
Is there any advantage to (while [ 1 ]; do sleep 1; done) | python test.py rather than the much simpler cat | python test.py described here?Pitfall
P
9

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.

Paluas answered 18/9, 2015 at 3:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.