Skip to content Skip to sidebar Skip to footer

Freeze Stdin When In The Background, Unfreeze It When In The Foreground

I am trying to run a sript in the background: nohup script.py > out 2> err < /dev/null & The script (Python 3.4) does at some point: answer = input('? ') (it has a m

Solution 1:

What you want (and how input works and fails on EOF under python) with an interactive menu means that you cannot safely pass a file as stdin when invoking your program. This means your only option is to invoke this like so:

$ script.py > out2> err &

As a demonstration, this is my script:

from time import sleep
import sys

c = 0whileTrue:
    sleep(0.001)
    c += 1if c % 1000 == 0:
        print(c, flush=True)
    if c % 2000 == 0:
        print(c, file=sys.stderr, flush=True)
    if c % 10000 == 0:
        answer = input('? ')
        print('The answer is %s' % answer, flush=True)

Essentially, every second it will write to stdout, every two seconds it will write to stderr, and lastly, every ten seconds it will wait for input. If I were to run this and wait a bit over a seconds (to allow disk flush), and chain this together, like so:

$ python script.py > out 2> err & sleep 2.5; cat out err
[1] 32123
1000
2000
2000
$ 

Wait at least 10 seconds and try cat out err again:

$catouterr10002000300040005000600070008000900010000?200040006000800010000

[1]+Stoppedpythonscript.py>out2>err$

Note that the prompt generated by input is also written to stdout, and the program effectively continued running up to where it is expecting stdin to give it data. You simply have to bring the process back into foreground by %, and start feeding it the required data, then suspend with ^Z (CtrlZ) and keep it running again in the background with %&. Example:

$ %
python script.py > out2> err
Test input
^Z
[1]+  Stopped                 python script.py > out2> err
$ %&
[1]+ python script.py > out2> err &
$

Now cat out again, after waiting another ten seconds:

$ cat out1000
...
10000
? The answer is Test input
11000
...
20000
? 
[1]+  Stopped                 python script.py > out2> err
$

This is essentially a basic crash course in how standard processes typically functions in both foreground and background, and things just simply work as intended if the code handles the standard IO correctly.

Lastly, you can't really have it both ways. If the application expects stdin and none is provided, then the clear option is failure. If one is provided however but application got sent to background and kept running, it will be Stopped as it expects further input. If this stopped behaviour is unwanted, the application is at fault, there is nothing can be done but to change the application to not result in an error when EOF is encountered when executed with /dev/null as its stdin. If you want to keep stdin as is, with the application being able to somehow keep running when it is in the background you cannot use the input function as it will block when stdin is empty (resulting in process being stopped).


Now that you have clarified via the comment below that your "interactive prompt" is running in a thread, and since usage of input directly reads from stdin and you seem unwilling to modify your program (you asking for general case) but expects a utility to do this for you, the simple solution is to execute this within a tmux or screen session as they fully implement a pseudo tty that is independent from whichever console that started (so you can disconnect and send the session to the background, or start other virtual sessions, see manual pages) which will provide stdio that the program expects.

Finally, if you actually want your application to support this natively, you cannot simply use input as is, but you should check whether input can safely be called (i.e. perhaps making use of select), or by checking whether the process is currently in the foreground or background (An example you could start working from is How to detect if python script is being run as a background process, although you might want to check using sys.stdin, maybe.) to determine if input can be safely called (however if user suspends the task as input comes it would still hang as input waits), or use unix sockets for communication.

Post a Comment for "Freeze Stdin When In The Background, Unfreeze It When In The Foreground"