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

+1


source to share


1 answer


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.

+1


source







All Articles