python subprocess.call () funktioniert nicht wie erwartet

11

Ich begann dieses Kaninchenloch, um mich mit der Erstellung eines Setup-Skripts in Python vertraut zu machen. Die Wahl von Python beruhte einfach auf meiner Vertrautheit damit, während ich sicher bin, dass es für diese Aufgabe bessere Alternativen als Python geben würde.

Das Ziel dieses Skripts war es, ROS auf dem Computer zu installieren, auf dem das Skript ausgeführt wird, und auch die Catkin-Umgebung einzurichten. Eine Wegbeschreibung finden Sie hier bzw. hier .

Das aktuelle Skript lautet wie folgt:

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"])

Wenn das Skript gerade ausgeführt wird, tritt ein Fehler mit dem Fehler auf:

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

Ich habe überprüft, ob der Befehl korrekt funktioniert, wenn er manuell über ein Terminalfenster ausgeführt wird. Daher glaube ich, dass dies ein grundlegendes Missverständnis darüber ist, wie dieses Skript und sein Umfang innerhalb des Betriebssystems behandelt werden. Der Teil, der mich sehr verwirrt, ist, warum er sich beschwert, dass das bereitgestellte Verzeichnis nicht gefunden werden kann, während ich überprüft habe, dass dieses Verzeichnis vorhanden ist. Wenn der Befehl eher aus Python gedruckt und in ein Terminalfenster eingefügt wird, treten keine Fehler auf.

Beeedy
quelle
Python hat seine eigenenos.chdir()
Jacob Vlijm
Wenn Sie Python 3 verwenden, übergeben Sie das cwdArgument einfach ancall
intsco

Antworten:

18

Standardmäßig subprocess.callwird keine Shell zum Ausführen unserer Befehle verwendet, daher können Sie keine Shell-Befehle wie ausführen cd.

Um eine Shell zum Ausführen Ihrer Befehle zu verwenden, verwenden Sie shell=Trueals Parameter. In diesem Fall wird empfohlen, Ihre Befehle als einzelne Zeichenfolge und nicht als Liste zu übergeben. Und da es von einer Shell ausgeführt wird, können Sie es auch ~/in Ihrem Pfad verwenden:

subprocess.call("(cd ~/catkin_ws/src && catkin_make)", shell=True)
Florian Diesch
quelle
1
Vielen Dank! Ich hatte den Eindruck, dass subprocess.call eine Shell verwendete, und wusste nicht, dass dies explizit angegeben werden musste. Der obige Befehl funktionierte genau wie beabsichtigt
Uhr
1
Warum nicht verwenden os.chdir()?
Jacob Vlijm
2
Wie wäre es subprocess.call(['catkin_make'], cwd=os.path.expanduser('~/catkin_ws/src'))?
Matt Nordhoff
shell=Trueruft die Standard-Shell auf, die dash ist. Wenn ein Skript, das OP enthält, Bashismen enthält, kann es beschädigt werden. Ich habe meiner Antwort eine Bearbeitung hinzugefügt. Eine alternative Lösung wäre, eine bestimmte Shell explizit aufzurufen. Besonders nützlich, wenn jemand mit csh-Skript zu
tun hat
Die beste Lösung ist der Vorschlag von Matt Nordhoff. Die Verwendung shell=True auch mit festen Befehlen führt zu Sicherheitslücken (z. B. kann auf einem anfälligen System ein Shellshock ausgelöst werden). Die Faustregel: Wenn Sie die Verwendung vermeiden können, solltenshell=True Sie sie vermeiden. Der cwdParameter ist genau dort, um die Art von Aufruf auszuführen, die das OP wünscht.
Bakuriu
5

subprocess.call() erwartet eine Liste, wobei das erste Element offensichtlich ein legitimer Shell-Befehl ist. Vergleichen Sie dies zum Beispiel:

>>> 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

In Ihrem Fall subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws/src)"])wird erwartet, dass eine Binärdatei gefunden wird, die so aussieht (beachten Sie den Backslash, der das Leerzeichen kennzeichnet):

 cd\ /home/user/catkin_ws/src

Dies wird als ein einziger Name behandelt, von dem erwartet werden kann, dass er irgendwo auf Ihrem System lebt. Was Sie wirklich tun möchten, ist:

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

Beachten Sie, dass ich Klammern um das Komma entfernt habe, da es keinen Grund gibt, eine Unterschale zu verwenden.

EDIT :

In den Kommentaren wurde jedoch bereits von progo erwähnt, dass die Verwendung cdin diesem Fall überflüssig ist. Florians Antwort erwähnt auch richtig, dass subprocess.call()keine Shell verwendet wird. Sie können dies auf zwei Arten angehen. Eins könnten Sie verwendensubprocess.call("command string",shell=True)

Die andere Möglichkeit besteht darin, eine bestimmte Shell explizit aufzurufen. Dies ist besonders nützlich, wenn Sie ein Skript ausführen möchten, für das eine bestimmte Shell erforderlich ist. So könnten Sie tun:

subprocess.call(['bash' , os.path.expanduser('~')  + "/catkin_ws/src"  ) ] )
Sergiy Kolodyazhnyy
quelle
1
call()erwartet keinen legitimen Shell-Befehl; Es wird erwartet, einen Pfad zu einer tatsächlich ausführbaren Datei zu finden. Und das Aufrufen eines Standalone-Systems cdbringt nichts: Das CWD ist eine prozessspezifische Variable, die nach dem Beenden des Prozesses nicht mehr existiert.
nperson325681
@progo guter Punkt, ich war so darauf konzentriert, den Befehl von OP zu bearbeiten, dass ich nicht einmal bemerkte, dass cdhier nichts passiert. . . . Aber was "legitim" subprocess.call()['ls -l']
betrifft
@ Progo machte eine kleine Bearbeitung, bitte überprüfen
Sergiy Kolodyazhnyy
3

Verwenden Sie os.chdir()stattdessen.

Abgesehen von den Problemen, die in den vorhandenen Antworten erwähnt werden, würde ich es nicht vorziehen shell=True, das subprocess.call()Verzeichnis zu verwenden oder hier zu wechseln.

Python hat seine eigene Art, das Verzeichnis zu wechseln os.chdir()(vergessen Sie nicht import os). ~( „Heimat“) kann auf verschiedene Weise, ua definiert werden os.environ["HOME"].

Gründe, dies vorzuziehen, shell=Truefinden Sie ua hier

Jacob Vlijm
quelle