hansaplast
hansaplast

Reputation: 11573

subprocess: start process in background and start another in one call

This program should echo the pid of sleep immediately:

import subprocess
subprocess.check_output("sleep 1 & echo $!", shell=True)

Running this on the shell directly, it immediately prints the pid, but running it in python, the & is ignored and it takes 1 second before echo is executed.

How can I get this to work with only one execution of check_output (or another function of subprocess)?

(This is a simplified example, in reality instead of sleep 1 I'd put my own executable)

Upvotes: 0

Views: 1176

Answers (1)

tdelaney
tdelaney

Reputation: 77337

check_output waits for the output pipes to close and sleep has them too. You can redirect to /dev/null for an immediate return.

subprocess.check_output("sleep 1 >/dev/null 2>&1 & echo $!", shell=True)

UPDATE

Its hard to tell if sleep 1 really did run in the background so I wrote a slightly larger test.

test.py - writes time to stdout for 5 seconds

import time

for i in range(5):
    print(time.strftime('%H:%M:%S'), flush=True)
    time.sleep(1)
print('done', flush=True)

runner.py - runs the test redirecting stdout to a file and monitors the file.

import subprocess as subp
import time
import os

# run program in background
pid = int(subp.check_output("python3 test.py >test.out 2>&1 & echo $!",
    shell=True))
print("pid", pid)

# monitor output file
pos = 0
done = False
while not done:
    time.sleep(.1)
    if os.stat('test.out').st_size > pos:
        with open('test.out', 'rb') as fp:
            fp.seek(pos)
            for line in fp.readlines():
                print(line.strip().decode())
                done = b'done' in line
            pos = fp.tell()
print("test complete")

Running it, I get

td@mintyfresh ~/tmp $ python3 runner.py
pid 24353
09:32:18
09:32:19
09:32:20
09:32:21
09:32:22
done
test complete

Upvotes: 1

Related Questions