Python script using subprocess and xclip hangs if connected via bash
I have a python script that needs to output some values ββto stdin and copy another string to the clipboard. I am using a module subprocess
to execute a utility xclip
through Popen
something like this:
# clip.py
import subprocess
from subprocess import Popen
print('PRINT_ME')
p1 = Popen(['xclip', '-selection', 'clipboard'], stdin=subprocess.PIPE)
p1.communicate(input=('PASTE_ME'.encode()))
The script works as intended: PRINT_ME
reflected in bash and PASTE_ME
available for insertion and returned imediatly.
The problem occurs when the output of the script is piped on another command. Let's say you want to redirect file and stdin with tee
:
$ python clip.py|tee file.txt
The program works as expected but does not return, i.e. the shell does not return.
How can this be fixed?
Some important information : the xclip
forks utility itself (to handle the X clipboard implementation), keeping the string available for copying when used in the shell returns immediately (these are background forks). It seems that both the shell attaches clip.py
and xclip
stdin / stdout to tee
.
If xclip -selection clipboard
found with ps u
and killed, the command returns.
I am using Python 3.4.
Thanks to
source to share
This is not because of forking xclip
, because of the way Python handles Popen.wait()
(which is called with your call to communicate()
) - from Python's point of view xclip
(silent by default) its thread does not close, so it waits ... That's why everything works. except that Python moves down your line p1.communicate()
while forwarding its streams to something else ( tee
in this case) - it waits for all of its files to be closed ..
You can manually open and close your streams, or just configure xclip
to filter STDIN in STDOUT and keep Python happy:
import subprocess
p1 = subprocess.Popen(['xclip', '-selection', 'clipboard', '-f'], stdin=subprocess.PIPE)
p1.communicate(input=('PASTE_ME'.encode()))
# etc.
Haven't tested but it should work. If you don't want it to be xclip
printed to your current STDOUT, just redirect it on creation subprocess.Popen()
before None
(or subprocess.DEVNULL
for Python 3.x) or whatever stream descriptor you want.
source to share