2

In my python script, I am trying to run a windows program that prints output. But I would like to redirect that output to a text file. I tried

     command = 'program' + arg1 + ' > temp.txt'
     subprocess.call(command)

Where program is my program name and arg1 is argument it takes. but it does not redirect the output to the text file It just prints that on the screen.

Can anyone help me how to do this? Thank you!

in His Steps
  • 3,075
  • 6
  • 30
  • 38

2 Answers2

9

Pass a file object to the stdout parameter of subprocess.call():

with open('myoutfilename', 'w') as myoutfile:
    subprocess.call(cmd, stdout=myoutfile)
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • +1 - get the library to do it for you. Also, [the docs](http://docs.python.org/2/library/subprocess.html#frequently-used-arguments) recommend you pass the program and arguments as a list so it can take care of the proper escaping for you - so the call we be something like `subprocess.call(['program', arg1], stdout=myoutfile])`. – Blair Jan 02 '13 at 21:23
  • @blair Indeed, cmd can be a list. The key point is use of the stdout parameter – David Heffernan Jan 02 '13 at 21:25
  • @DavidHeffernan -- cmd **must** be a list if you're using program has arguments (and you're not using `shell=True`), but you're right about `stdout` being one of the key pieces to this. – mgilson Jan 02 '13 at 21:30
  • Sure. Question is about redirect, hence the focus of the answer. – David Heffernan Jan 02 '13 at 21:33
  • @mgilson: That's only true on Unix. On Windows, `cmd` *can* be a string, even if there are arguments—and, in fact, quoting [the docs](http://docs.python.org/library/subprocess.html#popen-constructor), "if *args* is a sequence, it will be converted to a string in a manner described in *Converting an argument sequence to a string on Windows*. This is because the underlying `CreateProcess()` operates on strings.` – abarnert Jan 02 '13 at 23:05
  • @abarnert -- Interesting. I didn't know that. For the most part, I tend to ignore Windows as an operating system :-p ... I just code according to the recommendations in the docs and cross my fingers when it comes to windows compatibility. – mgilson Jan 03 '13 at 00:32
  • @mgilson: I envy you. I have to read that stuff or it's not going to work for half our users. And then I have to figure out how to work around the fact that `CreateProcess` quoting doesn't work sensibly, there's no way to do atomic write-temp-and-rename, you can't toss a pipe into a select for clean shutdown, … – abarnert Jan 03 '13 at 00:52
5

You can use shell=True in subprocess.call

However, a (much) better way to do this would be:

command = ['program',arg1]
with open('temp.txt','w') as fout:
    subprocess.call(command,stdout=fout)

This removes the shell from the whole thing making it more system independent, and it also makes your program safe from "shell injection" attacks (consider arg1='argument; rm -rf ~' or whatever the windows equivalent is).

The context manager (with statement) is a good idea as it guarantees that your file object is properly flushed and closed when you leave the "context".

Note that it is important that if you're not using shell=True to a subprocess.Popen (or similar) class, you should pass the arguments as a list, not a string. Your code will be more robust that way. If you want to use a string, python provides a convenience function shlex.split to split a string into arguments the same way your shell would. e.g.:

 import subprocess
 import shlex
 with open('temp.txt','w') as fout:
     cmd = shlex.split('command argument1 argument2 "quoted argument3"'
     #cmd = ['command', 'argument1', 'argument2', 'quoted argument3']
     subprocess.call(cmd,stdout=fout)
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
mgilson
  • 300,191
  • 65
  • 633
  • 696
  • Please do feel free to remove the version that doesn't use a context manager. There's no point leading people astray. I draw no pleasure in seeing such code perpetuated. – David Heffernan Jan 02 '13 at 21:45
  • @DavidHeffernan -- Done. Though it was a bit of a challenge to re-work the wording to give you credit where it was due. – mgilson Jan 02 '13 at 21:57
  • Well, thanks. But I deleted that. It's just best to see good and accurate answers. – David Heffernan Jan 02 '13 at 22:19
  • @DavidHeffernan -- I noticed (and resisted to urge to repost it as a comment). In my line of work, credit means everything, so editing a post to absorb another's ideas seems wrong without proper "citation" or whatever. I've thought about deleting my answer as well, but it does add quite a bit to your answer so I haven't done so (but you're more than welcome to take whatever you like from my answer and add it to yours). Cheers, and I'm glad you got the checkmark :) – mgilson Jan 02 '13 at 22:34