0

I have to run a "commit" command through python script and print a message based on its exit or return status.

code is as follows:

import subprocess

msg = 'summary about commit'
commitCommand = 'hg commit -m "{}"'.format(msg)

p = subprocess.Popen(commitCommand, stdout=subprocess.PIPE)
output = p.communicate()[0]

if p.returncode:
    print 'commit failed'
    sys.exit()
else:
    print 'Commit done'

This is giving me following error:

Traceback (most recent call last):
  File "script.py", line 66, in <module>
    p = subprocess.Popen(commitCommand, stdout=subprocess.PIPE)
  File "/usr/lib/python2.7/subprocess.py", line 711, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1308, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory

how to correct this error?

Mike
  • 11
  • 4
  • 2
    `import shlex; ...;subprocess.Popen(shlex.split(commitCommand), ...)`. [Related question](http://stackoverflow.com/q/21029154/510937), documentation for [`Popen`](http://docs.python.org/2/library/subprocess.html#subprocess.Popen) (which you should read). – Bakuriu Jan 10 '14 at 09:00
  • @Bakuriu: Why use `shlex.split()` when the OP is *building the string*? – Martijn Pieters Jan 10 '14 at 10:13

3 Answers3

0

From the docs;

args should be a sequence of program arguments or else a single string. By default, the program to execute is the first item in args if args is a sequence. If args is a string, the interpretation is platform-dependent and described below. See the shell and executable arguments for additional differences from the default behavior. Unless otherwise stated, it is recommended to pass args as a sequence.

On Unix, if args is a string, the string is interpreted as the name or path of the program to execute. However, this can only be done if not passing arguments to the program.

So, it's looking for a file hg commit -m "{}" and the .format(msg). Popen wants a list, the fist element being "hg", or better, a real path.

Or set shell=True in Popen (this all from the docs, not pretending to actually test this very often) and get a Popen(['/bin/sh', '-c', args[0], args[1], ...]) effect.

Bakuriu's comment advice is a nice safe bet though, use shlex.

Community
  • 1
  • 1
Brian Tiffin
  • 3,978
  • 1
  • 24
  • 34
0

The aforesaid process is much safer to use... but alternatively there would be dirty ways to do any thing...

Instead of splitting the command into an array of strings... you can also use shell=True along with stdout = subprocess.PIPE.

But this is what python says about using shell = True.

Warning Passing shell=True can be a security hazard if combined with untrusted input. See the warning under Frequently Used Arguments for details.

If you are not using shell = True and giving a command in a string, it throws the above error you got because the first command it looks for is the shell path and the you passed hg which is not existing.
But use shell = True wisely.

P.S. Be aware that you have been warned :P

Sravan K Ghantasala
  • 1,058
  • 8
  • 14
0

You are not using shell=True, in which case you need to pass in the command and its arguments preparsed, as a list:

commitCommand = ['hg', 'commit', '-m', msg]

This also means you don't need to quote the message; that's only needed when using a shell and you want to pass in the whole message as one argument.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343