Mike Williamson
Mike Williamson

Reputation: 3178

Python subprocess: why won't the list of arguments work analogous to the full shell string?

Thanks in advance for any help. I am new to python, but not particularly new to scripting. I am trying to run a simple, automated email program, but the email module seems to be installed incorrectly on our system (I don't have 75% of the functions described in the python examples, only "message_from_string" and "message_from_file") and smtplib is overly complicated for what I need.

In fact, in simple bash terms, all I need is:

/bin/email -s "blah" "recipients" < file.with.body.info.txt

or,

echo "my body details" | /bin/email -s "blah" "recipients"

so that I can avoid having to write to a file just to send a message.

I tried using subprocess, either call or Popen, and the only way I could eventually get things to work is if I used:

subprocess.call('/bin/mail -s "blah" "recipients" < file.with.body.info.txt', shell=True)

A few things I specifically don't like about this method:

(1) I couldn't break things into a list or tuple, as it is supposed to work, so that I lost the whole advantage of subprocess, as I understand it, in keeping things secure. If I tried:

subprocess.call(['/bin/mail', '-s', subjVariable, recipVariable, '<', 'file.with.body.info.txt'], shell=True)

it would fail. Similarly, if I tried to use the pipe, '|', instead of reading from a file, it would fail. It was also failing if I used '-cmd' instead of a pipe. The "fail" was usually that it would read '<' and 'file.with.body.info.txt' as if they were further recipients. In other words, whether I said "shell = True" or not, subprocess was not able to interpret the special characters in the call as the special characters that they are. '<' wasn't recognized as an input from a file, etc., unless I kept everything in one large call.

What I would ideally like to be able to do, because it seems more secure, as well as more flexible, is something like this:

subprocess.call(['/bin/echo', varWithBody, '|', '/bin/mail', '-s', subjVariable, recipVariable,])

but it seems that pipes are not understood at all with subprocess and I cannot figure out how to pipe things together while stuck behind python.

Any suggestions? All help is welcome, except attempts to explain how to use the 'email' or 'smtplib' modules. Regardless of this particular application, I really want to learn how to use subprocess better, so that I can tie together disparate programs. My understanding is that python should be fairly decent at that.

Thanks! Mike

Upvotes: 4

Views: 3613

Answers (4)

Steve V.
Steve V.

Reputation: 453

The Python docs seem to cover this situation.

What I'd probably do is something like the following

from subprocess import *
readBody = Popen(["/bin/echo", varWithBody], stdout=PIPE)
mail = Popen(["/bin/mail", "-s", subjVariable, recipVariable], stdin=readBody.stdout, stdout=PIPE)
output = mail.communicate()[0]

Upvotes: 5

david4dev
david4dev

Reputation: 4914

| and < are not arguments; they are shell redirections. To replace the | in your code, see these instructions.

To replace <, use:

 subprocess.Popen(["command", "args"], stdin=open("file.txt", 'r'))

eg.

subprocess.Popen(["cat"], stdin=open("file.txt", 'r')) is the same as cat < file.txt

Upvotes: 5

Jason Baker
Jason Baker

Reputation: 198547

You need to run the command through the shell using the shell argument:

>>> import subprocess
>>> subprocess.call('ls -a | cat', shell=True)
.
..
.git
.gitignore
doc
generate_rands.py
infile1
infile2
infile3
matrix.pyc
matrix.py~
median.py
problems
simple_median.py
test
test_matrix.py
test_matrix.py~
test_median.py

Upvotes: 2

Matti Virkkunen
Matti Virkkunen

Reputation: 65126

<, | etc are features of the shell, not the operating system. Therefore something like subprocess won't know anything about them - internally it's just passing the list of arguments to the equivalent OS functions. The way to do input/output redirection using subprocess is using the stdin, stdout and strerr parameters. You can pass in a file object (it has to contain a file descriptor, though, but normally opened files always do) or a naked file descriptor. Or a pipe object.

The manual has an example for replacing a pipeline, just replace the pipe with a file object and you should be all set.

Upvotes: 4

Related Questions