user3363550
user3363550

Reputation: 49

why is this linux command not working when executed using python?

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

Answers (2)

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

Bakuriu
Bakuriu

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

Related Questions