web-dev-qa-db-fra.com

Erreur de fichier introuvable lors du lancement d'un sous-processus contenant des commandes redirigées

J'ai besoin d'exécuter la commande date | grep -o -w '"+tz+"'' | wc -w en utilisant Python sur mon hôte local. J'utilise le module subprocess pour le même et j'utilise le check_output méthode car j'ai besoin de capturer la sortie pour la même chose.

Cependant, cela me lance une erreur:

Traceback (most recent call last):
  File "test.py", line 47, in <module>
    check_timezone()
  File "test.py", line 40, in check_timezone
    count = subprocess.check_output(command)
  File "/usr/lib/python2.7/subprocess.py", line 537, in check_output
    process = Popen(stdout=PIPE, *popenargs, **kwargs)
  File "/usr/lib/python2.7/subprocess.py", line 679, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1249, in _execute_child
    raise child_exception-
OSError: [Errno 2] No such file or directory

S'il vous plaît, aidez-moi où je me trompe. Je suis nouveau sur python

33
h4ck3d

Vous devez ajouter Shell=True Pour exécuter une commande Shell. check_output Tente de trouver un exécutable appelé: date | grep -o -w '"+tz+"'' | wc -w Et il ne le trouve pas. (aucune idée de la raison pour laquelle vous avez supprimé les informations essentielles du message d'erreur).

Voyez la différence entre:

>>> subprocess.check_output('date | grep 1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.4/subprocess.py", line 603, in check_output
    with Popen(*popenargs, stdout=PIPE, **kwargs) as process:
  File "/usr/lib/python3.4/subprocess.py", line 848, in __init__
    restore_signals, start_new_session)
  File "/usr/lib/python3.4/subprocess.py", line 1446, in _execute_child
    raise child_exception_type(errno_num, err_msg)
FileNotFoundError: [Errno 2] No such file or directory: 'date | grep 1'

Et:

>>> subprocess.check_output('date | grep 1', Shell=True)
b'gio 19 giu 2014, 14.15.35, CEST\n'

Lisez la documentation sur les arguments fréquemment utilisés pour plus d'informations sur l'argument Shell et comment il modifie l'interprétation des autres arguments.


Notez que vous devez essayer d'éviter d'utiliser Shell=True Car la génération d'un Shell peut être un danger pour la sécurité (même si vous n'exécutez pas d'attaques d'entrée non fiables comme Shellshock peut toujours être effectuée!).

La documentation du module de sous-processus contient une petite section sur remplacement du pipeline Shell . Vous pouvez le faire en générant les deux processus dans python et utilisez subprocess.PIPE:

date_proc = subprocess.Popen(['date'], stdout=subprocess.PIPE)
grep_proc = subprocess.check_output(['grep', '1'], stdin=date_proc.stdout, stdout=subprocess.PIPE)
date_proc.stdout.close()
output = grep_proc.communicate()[0]

Vous pouvez écrire une fonction wrapper simple pour définir facilement des pipelines:

import subprocess
from shlex import split
from collections import namedtuple
from functools import reduce

proc_output = namedtuple('proc_output', 'stdout stderr')


def pipeline(starter_command, *commands):
    if not commands:
        try:
            starter_command, *commands = starter_command.split('|')
        except AttributeError:
            pass
    starter_command = _parse(starter_command)
    starter = subprocess.Popen(starter_command, stdout=subprocess.PIPE)
    last_proc = reduce(_create_pipe, map(_parse, commands), starter)
    return proc_output(*last_proc.communicate())

def _create_pipe(previous, command):
    proc = subprocess.Popen(command, stdin=previous.stdout, stdout=subprocess.PIPE)
    previous.stdout.close()
    return proc

def _parse(cmd):
    try:
        return split(cmd)
    except Exception:
        return cmd

Avec cela en place, vous pouvez écrire pipeline('date | grep 1') ou pipeline('date', 'grep 1') ou pipeline(['date'], ['grep', '1'])

68
Bakuriu

D'après mon expérience, la cause la plus courante de FileNotFound avec un sous-processus est l'utilisation d'espaces dans votre commande. Utilisez plutôt une liste.

# Wrong, even with a valid command string
subprocess.run(["date | grep -o -w '\"+tz+\"' | wc -w"])

# Fixed
subprocess.run(["date", "|", "grep", "-o", "-w", "'\"+tz+\"'", "|", "wc", "-w"])

Cette modification n'entraîne plus d'erreurs FileNotFound et constitue une bonne solution si vous êtes arrivé à la recherche de cette exception avec une commande plus simple. Si vous utilisez python 3.5 ou supérieur, essayez d'utiliser cette approche:

import subprocess

a = subprocess.run(["date"], stdout=subprocess.PIPE)
print(a.stdout.decode('utf-8'))

b = subprocess.run(["grep", "-o", "-w", "'\"+tz+\"'"],
                   input=a.stdout, stdout=subprocess.PIPE)
print(b.stdout.decode('utf-8'))    

c = subprocess.run(["wc", "-w"],
                   input=b.stdout, stdout=subprocess.PIPE)
print(c.stdout.decode('utf-8'))

Vous devriez voir comment la sortie d'une commande devient l'entrée d'une autre, tout comme l'utilisation d'un tube Shell, mais vous pouvez facilement déboguer chaque étape du processus en python. L'utilisation de subprocess.run est recommandée pour python> 3.5, mais n'est pas disponible dans les versions antérieures.

5
mightypile