143
subprocess.call(["/home/myuser/run.sh", "/tmp/ad_xml",  "/tmp/video_xml"])

RIght now I have a script that I run. When I run it and it hits this line, it starts printing stuff because run.sh has prints in it.

How do I pipe this to a text file also? (And also print, if possible)

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
TIMEX
  • 259,804
  • 351
  • 777
  • 1,080

2 Answers2

251

If you want to write the output to a file you can use the stdout-argument of subprocess.call.

It takes either

  • None (the default, stdout is inherited from the parent (your script))
  • subprocess.PIPE (allows you to pipe from one command/process to another)
  • a file object or a file descriptor (what you want, to have the output written to a file)

You need to open a file with something like open and pass the object or file descriptor integer to call:

f = open("blah.txt", "w")
subprocess.call(["/home/myuser/run.sh", "/tmp/ad_xml",  "/tmp/video_xml"], stdout=f)

I'm guessing any valid file-like object would work, like a socket (gasp :)), but I've never tried.

As marcog mentions in the comments you might want to redirect stderr as well, you can redirect this to the same location as stdout with stderr=subprocess.STDOUT. Any of the above mentioned values works as well, you can redirect to different places.

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
Skurmedel
  • 21,515
  • 5
  • 53
  • 66
  • 11
    why doesn't subprocess.call(["echo", "1", ">>", "t.txt"]) work? – JobHunter69 Jul 18 '17 at 10:57
  • 15
    @Goldname you need a shell to do redirection, so: `subprocess.call(["echo", "1", ">>", "t.txt"], shell=True)` should work. – Wes Mason Jul 19 '17 at 20:17
  • 1
    @WesMason Thanks, but i thought subprocess.call worked in a way that was basically like copying and pasting into the shell? – JobHunter69 Jul 19 '17 at 20:43
  • 4
    @Goldname nah, everything in subprocess relies on the underlying Popen class, which opens an process, shell=True will force the process to be executed in the system default shell (e.g. /bin/sh on POSIX systems, which is usually bash or dash), it's more secure that way as you're not open to all of the extra functionality provided by the shell (a whole programming language unto itself, with it's own exploits if passing in user provided data). Also take a look at `shlex.quote` if you are passing any possibly dirty data to a subshell. – Wes Mason Jul 19 '17 at 20:48
  • 5
    Should we also close the file after we are done? this will close the file after we are done: `with open("blah.txt","w") as f: subprocess.call(["/home/myuser/run.sh", "/tmp/ad_xml", "/tmp/video_xml"], stdout=f) ` @Skurmedel – alper Sep 12 '18 at 14:50
  • I think this solution prevents the parent process from exiting because it needs to wait until the child process finish to close the file. – Michael Sep 20 '19 at 15:20
  • Works for me. I also use a context manager. Did the trick :D thanks! – Hajar Razip Jul 13 '23 at 07:11
26

The options for popen can be used in call

args, 
bufsize=0, 
executable=None, 
stdin=None, 
stdout=None, 
stderr=None, 
preexec_fn=None, 
close_fds=False, 
shell=False, 
cwd=None, 
env=None, 
universal_newlines=False, 
startupinfo=None, 
creationflags=0

So...

myoutput = open('somefile.txt', 'w')
subprocess.call(["/home/myuser/run.sh", "/tmp/ad_xml",  "/tmp/video_xml"], stdout=myoutput)

Then you can do what you want with myoutput

Also, you can do something closer to a piped output like this.

dmesg | grep hda

would be:

p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]

There's plenty of lovely, useful info on the python manual page.

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
ocodo
  • 29,401
  • 18
  • 105
  • 117