Python subprocess - write some stdin

I need to open an R script and provide it using input written in a separate python script. The subprocess module seems to be a good way to do this.

I came across some cryptic results, namely that I can write once and only once through p.stdin . Here is what I still have:

 from subprocess import Popen, PIPE, STDOUT p = Popen(['r --no-save'],stdin=PIPE,stdout=PIPE,stderr=PIPE,shell=True) p.stdin.write("source('myrscript.R')\n") p.stdin.write('myfirstinput') 

What happens when I run this code is that the first instance of stdin.write() executes as expected (and opens my R script), but the second line does nothing, and the subprocess (actually, R script) exits with an error, indicating that the subprocess did not receive any input, where the input was expected and, therefore, was terminated.

NB - In an ideal world, I would just interact directly through R, but this particular script requires complex resources that cannot be entered directly for practical purposes. In addition, rpy / rpy2 is not an option, as the end users of this script will not necessarily have access to this module or its dependencies. rscript also not an option (for many reasons, but mainly because of the variability in R end-user configurations).

Finally, p.communicate not an option, because apparently this will close the process after writing, and I will need to open it.

Thanks in advance

+7
source share
1 answer

You need to call .communicate() :

 from subprocess import Popen, PIPE, STDOUT p = Popen( ['r', '--nosave'], stdin=PIPE, stdout=PIPE, stderr=PIPE) p.stdin.write("source('myrscript.R')\n") p.stdin.write('myfirstinput\n') p.stdin.write('q\n') stdout, stderr = p.communicate() print '---STDOUT---' print stdout print '---STDERR---' print stderr print '---' 

Discussion

  • I do not use shell=True and it seems to work with my fake R script, since I do not have an R installation on my system. You may need this.
  • I prefer to split the command line up into a list of lines as shown, but a single line will work, such as r --nosave ; just don't do them both at the same time.
  • Do not forget that stdin.write() does not write the new character of the string \n , you must provide this yourself.

Update

My first attempt was uneasy, I hope this second attempt comes closer. As suggested by Yu. F. Sebastian, you can use pexpect :

 import pexpect import sys if __name__ == '__main__': prompt = '> ' # Don't know what the R prompt looks like lines = ['one', 'two', 'three'] r = pexpect.spawn('r --no-save', logfile=sys.stdout) for line in lines: r.expect(prompt) r.sendline(line) # If you want to interact with your script, use these two lines # Otherwise, comment them out r.logfile = None # Turn off logging to sys.stdout r.interact() 

Discussion

  • You may need to install pexpect . I did this with pip install pexpect
  • If you do not want to interact with the system, comment out the last two lines, but do not forget to send some signal to exit the R script.
  • spawn() returns a spawn object, see doc here .
+3
source

All Articles