output the command line called by subprocess? output the command line called by subprocess? python python

output the command line called by subprocess?


It depends on the version of Python you are using. In Python3.3, the arg is saved in proc.args:

proc = subprocess.Popen(....)print("the commandline is {}".format(proc.args))

In Python2.7, the args not saved, it is just passed on to other functions like _execute_child. So, in that case, the best way to get the command line is to save it when you have it:

proc = subprocess.Popen(shlex.split(cmd))print "the commandline is %s" % cmd

Note that if you have the list of arguments (such as the type of thing returned by shlex.split(cmd), then you can recover the command-line string, cmd using the undocumented function subprocess.list2cmdline:

In [14]: import subprocessIn [15]: import shlexIn [16]: cmd = 'foo -a -b --bar baz'In [17]: shlex.split(cmd)Out[17]: ['foo', '-a', '-b', '--bar', 'baz']In [18]: subprocess.list2cmdline(['foo', '-a', '-b', '--bar', 'baz'])Out[19]: 'foo -a -b --bar baz'


The correct answer to my question is actually that there IS no command line. The point of subprocess is that it does everything through IPC. The list2cmdline does as close as can be expected, but in reality the best thing to do is look at the "args" list, and just know that that will be argv in the called program.


Beautiful and scalable method

I have been using something like this:

#!/usr/bin/env python3import osimport shleximport subprocessimport sysdef run_cmd(cmd, cwd=None, extra_env=None, extra_paths=None, dry_run=False):    if extra_env is None:        extra_env = {}    newline_separator = ' \\\n'    out = []    kwargs = {}    env = os.environ.copy()    # cwd    if 'cwd' is not None:        kwargs['cwd'] = cwd    # extra_env    env.update(extra_env)    for key in extra_env:        out.append('{}={}'.format(shlex.quote(key), shlex.quote(extra_env[key])) + newline_separator)    # extra_paths    if extra_paths is not None:        path = ':'.join(extra_paths)        if 'PATH' in env:            path += ':' + env['PATH']        env['PATH'] = path        out.append('PATH="{}:${{PATH}}"'.format(':'.join(extra_paths)) + newline_separator)    # Command itself.    for arg in cmd:        out.append(shlex.quote(arg) + newline_separator)    # Print and run.    kwargs['env'] = env    print('+ ' + '  '.join(out) + ';')    if not dry_run:        subprocess.check_call(cmd, **kwargs)run_cmd(    sys.argv[1:],    cwd='/bin',    extra_env={'ASDF': 'QW ER'},    extra_paths=['/some/path1', '/some/path2'])

Sample run:

./a.py echo 'a b' 'c d' 

Output:

+ ASDF='QW ER' \  PATH="/some/path1:/some/path2:${PATH}" \  echo \  'a b' \  'c d' \;a b c d

Feature summary:

  • makes huge command lines readable with one option per line
  • add a + to commands like sh -x so users can differentiate commands from their output easily
  • show cd, and extra environment variables if they are given to the command. These only printed if given, generating a minimal shell command.

All of this allows users to easily copy the commands manually to run them if something fails, or to see what is going on.

Tested on Python 3.5.2, Ubuntu 16.04. GitHub upstream.