Reputation: 49
I want to execute this linux command
"cat cflow_image.py | mailx -s "CFLOW Copy" [email protected]". My requirement is to use this command in the python script.I am using subprocess module to achieve this.
Here is my piece of code for doing this,
def send_mail(mailid):
# This is mail the testbed info to the user
mailid = args.mailID
print "* INFO file will be sent to your mailid *"
subprocess.call("cat info.txt | mailx -s \"DEVSETUP\" {0}").format(mailid)
Following is the error when it is executed,
Traceback (most recent call last):
File "dev-installer.py", line 243, in <module>
send_mail(args.mailID)
File "dev-installer.py", line 204, in send_mail
subprocess.call("cat info.txt | mailx -s \"DEVSETUP\" {0}").format(mailid)
File "/sw/packages/python/2.7.4/lib/python2.7/subprocess.py", line 524, in call
return Popen(*popenargs, **kwargs).wait()
File "/sw/packages/python/2.7.4/lib/python2.7/subprocess.py", line 711, in __init__
errread, errwrite)
File "/sw/packages/python/2.7.4/lib/python2.7/subprocess.py", line 1308, in _execute_child
Upvotes: 3
Views: 1316
Reputation: 107739
You're passing a shell command, so Python should invoke a shell which would parse and run your command. But you're telling Python to invoke a command whose name is cat info.txt | mailx -s "DEVSETUP" [email protected]
, i.e. there should be an executable by that name on the command search path — and no such command exists.
subprocess.call
supports all keyword arguments of the Popen
constructor. You can pass the keyword argument shell=True
to indicate that what you have is some shell code and not the name of an executable file.
subprocess.call("cat info.txt | mailx -s \"DEVSETUP\" {0}".format(mailid),
shell=True)
However, beware that the value of mailid
will be interpolated in the shell snippet, which will break if it contains shell special characters, as in [email protected] (Bob Smith)
or Bob Smith <[email protected]>
. You should arrange to quote any special character that is present in mailid
.
Another problem with the command you posted is that if any error occurs while reading info.txt
, it won't be detected. You can remedy that by avoiding the useless use of cat
:
subprocess.call("<info.txt mailx -s \"DEVSETUP\" {0}".format(mailid),
shell=True)
Given that the command is simple, you don't need to invoke a shell at all. If no shell gets involved, you don't need to worry about quoting. You can easily make Python open the info.txt
file for reading.
body_file = open("info.txt")
status = subprocess.call(["mailx", "-s", "DEVSETUP", mailid], stdin=body_file)
body_file.close()
if status != 0:
… handle mailx failure …
Upvotes: 1
Reputation: 101909
subprocess.call
executes an executable. You are not passing a path to an executable as argument, you are passing a shell command line.
To execute a shell command you have to explicitly state that you want to execute the command in a shell by passing shell=True
in the argument list.
Note that using shell=True
with user-provided commands can be a securiy hazard.
Also note that when you are not using shell=True
(e.g. when you don't specify it), you should pass a list of strings as argument which represent the already parsed argument list.
For example:
subprocess.call('ls -l')
Will try to execute a command named ls -l
and will probably fail if you don't have an executable with that name in the PATH
, while:
subprocess.call(['ls', '-l'])
will call the ls
executable with the argument -l
.
If you don't want to manually write such lists use shlex.split
to parse the "command line":
subprocess.call(shlex.split('executable arg1 arg2 --option1 --option2 "string argument"'))
Will result in the call:
subprocess.call(['executable', 'arg1', 'arg2', '--option1', '--option2', 'string argument'])
Note how the quotes were properly handled (however no shell expansion is performed!)
Upvotes: 3