web-dev-qa-db-fra.com

python subprocess.call () ne fonctionne pas comme prévu

J'ai commencé par ce lapin pour me familiariser avec la manière de créer un script d'installation en python. Le choix de python était simplement ancré dans ma connaissance du logiciel alors que je suis sûr qu'il y aurait de meilleures alternatives que python pour cette tâche.

Le but de ce script était d'installer ROS sur la machine sur laquelle il s'exécutait et de configurer l'environnement Catkin. Les directions peuvent être trouvées ici et ici , respectivement.

Le script tel qu'il se présente actuellement est le suivant:

subprocess.call(["Sudo", "sh", "-c", "'echo \"deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main\" > /etc/apt/sources.list.d/ros-latest.list'"])
subprocess.call(["Sudo", "apt-key", "adv", "--keyserver", "hkp://ha.pool.sks-keyserver.net:80", "--recv-key", "0xB01FA116"])
subprocess.call(["Sudo", "apt-get", "update"])
subprocess.call(["Sudo", "apt-get", "install", "ros-kinetic-desktop-full", "-y"])
subprocess.call(["Sudo", "rosdep", "init"])
subprocess.call(["rosdep", "update"])
subprocess.call(["echo", '"source /opt/ros/kinetic/setup.bash"', ">>", "~/.bashrc", "source", "~/.bashrc"])
subprocess.call(["Sudo", "apt-get", "install", "python-rosinstall", "-y"])
mkdir_p(os.path.expanduser('~') + "/catkin_ws/src")
subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws/src)"])
subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws && catkin_make)"])
subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws && source devel/setup.bash"])

Lorsque le script est en cours d'exécution, le message d'erreur suivant s'affiche:

Traceback (most recent call last):
  File "setup.py", line 46, in <module>
    subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws/src)"])
  File "/usr/lib/python2.7/subprocess.py", line 523, in call
    return Popen(*popenargs, **kwargs).wait()
  File "/usr/lib/python2.7/subprocess.py", line 711, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1343, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory

J'ai vérifié que la commande fonctionnait correctement lorsqu'elle était exécutée manuellement à partir d'une fenêtre de terminal. En tant que telle, j'estime qu'il s'agit d'un malentendu fondamental sur la manière dont ce script et sa portée sont gérés dans le système d'exploitation. La partie qui me cause beaucoup de confusion est la raison pour laquelle il se plaint de ne pas pouvoir localiser le répertoire fourni, alors que j’ai vérifié que ce répertoire existe. Lorsque la commande est plutôt imprimée à partir de python et collée dans une fenêtre de terminal, aucune erreur n'est rencontrée.

8
beeedy

Par défaut, subprocess.call n'utilise pas de shell pour exécuter nos commandes. Vous ne pouvez donc pas utiliser de commandes telles que cd.

Pour utiliser un shell afin d’exécuter vos commandes, utilisez Shell=True en tant que paramètre. Dans ce cas, il est recommandé de transmettre vos commandes sous forme de chaîne unique plutôt que sous forme de liste. Et comme il est exécuté par un shell, vous pouvez également utiliser ~/ dans votre chemin:

subprocess.call("(cd ~/catkin_ws/src && catkin_make)", Shell=True)
13
Florian Diesch

subprocess.call() attend une liste, le premier élément étant évidemment une commande Shell légitime. Comparez ceci par exemple:

>>> subprocess.call(['echo hello'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/subprocess.py", line 523, in call
    return Popen(*popenargs, **kwargs).wait()
  File "/usr/lib/python2.7/subprocess.py", line 711, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1343, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory
>>> subprocess.call(['echo', 'hello'])
hello
0

Dans votre cas, subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws/src)"]) s'attendra à trouver un binaire qui ressemble à so (notez la barre oblique inverse désignant le caractère d'espace):

 cd\ /home/user/catkin_ws/src

Ce nom est traité comme un nom unique qui devrait figurer quelque part sur votre système. Ce que vous voulez vraiment faire est:

 subprocess.call(["cd", os.path.expanduser('~') + "/catkin_ws/src"])

Notez que j'ai supprimé les parenthèses autour de la virgule, car il n'y a aucune raison d'utiliser le sous-shell.

EDIT:

Mais progo a déjà mentionné dans les commentaires que l’utilisation de cd dans ce cas est redondante. La réponse de Florian mentionne également à juste titre que subprocess.call() n'utilise pas Shell. Vous pouvez aborder cela de deux manières. Un, vous pouvez utiliser subprocess.call("command string",Shell=True)

L’autre façon est d’appeler explicitement un shell spécifique. Ceci est particulièrement utile si vous souhaitez exécuter un script nécessitant un shell spécifique. Ainsi vous pourriez faire:

subprocess.call(['bash' , os.path.expanduser('~')  + "/catkin_ws/src"  ) ] )
5
Sergiy Kolodyazhnyy

Utilisez os.chdir() à la place.

Mis à part les problèmes mentionnés dans les réponses existantes, je ne préférerais pas utiliser Shell=True, ni subprocess.call() ici pour changer de répertoire.

Python a sa propre façon de changer de répertoire dans os.chdir() (n'oubliez pas de import os). ~ ("home") peut être défini de plusieurs manières, a.o. os.environ["HOME"].

Les raisons de préférer cela à Shell=True peuvent être lues a.o. ici

3
Jacob Vlijm