web-dev-qa-db-fra.com

afficher la ligne de commande appelée par sous-processus?

J'utilise le subprocess.Popen appeler, et dans une autre question, j'ai découvert que j'avais mal compris comment Python générait des arguments pour la ligne de commande.

Ma question
Existe-t-il un moyen de savoir quelle était la ligne de commande réelle?

Exemple de code: -

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

Comment écririez-vous getCommandLine?

42
Brian Postow

Cela dépend de la version de Python que vous utilisez. En Python3. , l'argument est enregistré dans proc.args:

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

En Python2.7, le args non enregistré , il est simplement passé à d'autres fonctions comme _execute_child. Donc, dans ce cas, la meilleure façon d'obtenir la ligne de commande est de l'enregistrer lorsque vous l'avez:

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

Notez que si vous avez la liste arguments (comme le type de chose retourné par shlex.split(cmd), alors vous pouvez récupérer la chaîne de ligne de commande, cmd en utilisant la fonction non documentée subprocess.list2cmdline:

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'
58
unutbu

La bonne réponse à ma question est qu'il n'y a pas de ligne de commande. Le point de sous-processus est qu'il fait tout via IPC. Le list2cmdline se rapproche le plus possible, mais en réalité la meilleure chose à faire est de regarder la liste "args", et sachez simplement que ce sera argv dans le programme appelé.

3
Brian Postow

Belle méthode évolutive

J'utilise quelque chose comme ça:

#!/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']
)

Exemple d'exécution:

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

Production:

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

Résumé des fonctionnalités:

  • rend les énormes lignes de commande lisibles avec une option par ligne
  • ajouter un + à des commandes comme sh -x pour que les utilisateurs puissent facilement différencier les commandes de leur sortie
  • show cd, et des variables d'environnement supplémentaires si elles sont données à la commande. Celles-ci ne s'impriment que si elles sont fournies, générant une commande Shell minimale.

Tout cela permet aux utilisateurs de copier facilement les commandes manuellement pour les exécuter en cas d'échec ou pour voir ce qui se passe.

Testé sur Python 3.5.2, Ubuntu 16.04. GitHub en amont .