¿Salida la línea de comando llamada por subproceso?

Estoy usando el subprocess.Popen Llamada subprocess.Popen , y en otra pregunta descubrí que había estado malinterpretando cómo Python estaba generando argumentos para la línea de comandos.

Mi pregunta
¿Hay alguna manera de averiguar cuál era la línea de comando real?

Código de ejemplo: –

 proc = subprocess.popen(....) print "the commandline is %s" % proc.getCommandLine() 

¿Cómo escribirías getCommandLine ?

Depende de la versión de Python que estés usando. En Python3.3 , el argumento se guarda en proc.args :

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

En Python2.7, los argumentos no se guardan , solo se pasan a otras funciones como _execute_child . Entonces, en ese caso, la mejor manera de obtener la línea de comandos es guardarla cuando la tenga:

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

Tenga en cuenta que si tiene la lista de argumentos (como el tipo de cosa devuelta por shlex.split(cmd) , entonces puede recuperar la cadena de la línea de comandos, cmd usando la función subprocess.list2cmdline no documentada:

 In [14]: import subprocess In [15]: import shlex In [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' 

La respuesta correcta a mi pregunta es en realidad que no hay una línea de comandos. El punto del subproceso es que lo hace todo a través de IPC. El list2cmdline hace lo más cercano posible, pero en realidad lo mejor que puede hacer es mirar la lista de “args”, y solo saber que eso será argv en el progtwig llamado.

Método hermoso y escalable

He estado usando algo como esto:

 #!/usr/bin/env python3 import os import shlex import subprocess import sys def 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'] ) 

Ejecución de la muestra:

 ./a.py echo 'ab' 'cd' 

Salida:

 + ASDF='QW ER' \ PATH="/some/path1:/some/path2:${PATH}" \ echo \ 'ab' \ 'cd' \ ; abcd 

Resumen de la característica:

  • hace que las grandes líneas de comando sean legibles con una opción por línea
  • agregue un + a comandos como sh -x para que los usuarios puedan diferenciar fácilmente los comandos de su salida
  • muestre cd y variables de entorno adicionales si se asignan al comando. Estos solo se imprimen si se dan, generando un comando de shell mínimo.

Todo esto permite a los usuarios copiar fácilmente los comandos manualmente para ejecutarlos si algo falla, o para ver qué está sucediendo.

Probado en Python 3.5.2, Ubuntu 16.04. GitHub aguas arriba .