web-dev-qa-db-ja.com

python subprocess.call()が期待どおりに機能しない

Pythonでセットアップスクリプトを作成する方法を理解するための手段として、このウサギの穴から始めました。 pythonの選択は、このタスクにpythonよりも優れた代替手段があると確信している間に、単にそれをよく知っていることに根ざしています。

このスクリプトの目的は、ROSをスクリプトを実行しているマシンにインストールし、catkin環境もセットアップすることでした。方向はそれぞれ here および here にあります。

現在のスクリプトは次のとおりです。

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

スクリプトが現在実行されている場合、次のエラーでエラーが発生します。

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

ターミナルウィンドウから手動で実行した場合、コマンドが正しく機能することを確認しました。したがって、これはこのスクリプトとそのスコープがOS内でどのように処理されるかについての基本的な誤解だと思います。私が多くの混乱を引き起こしているのは、このディレクトリが存在することを確認している間に、提供されたディレクトリを見つけることができないと文句を言う理由です。コマンドがpythonから印刷され、ターミナルウィンドウに貼り付けられた場合、エラーは発生しません。

10
beeedy

デフォルトでは、subprocess.callはコマンドを実行するのにシェルを使用しないため、cdのようなシェルコマンドは使用できません。

シェルを使用してコマンドを実行するには、Shell=Trueをパラメーターとして使用します。その場合、コマンドをリストではなく単一の文字列として渡すことをお勧めします。また、シェルによって実行されるため、パスで~/を使用することもできます。

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

subprocess.call()にはリストが必要です。最初の項目は明らかに正当なシェルコマンドです。たとえば、これを比較してください:

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

あなたの場合、subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws/src)"])は次のようなバイナリを見つけることを期待します(バックスラッシュがスペース文字を指定していることに注意してください):

 cd\ /home/user/catkin_ws/src

これは、システム上のどこかに存在すると予想される単一の名前として扱われます。本当にやりたいことは:

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

サブシェルを使用する理由がないため、コンマの周りの括弧を削除したことに注意してください。

編集

しかし、この場合cdを使用することは冗長であるというコメントでprogoによってすでに言及されています。 Florianの回答は、subprocess.call()はシェルを使用しないことも適切に言及しています。これには2つの方法でアプローチできます。 1つは、subprocess.call("command string",Shell=True)を使用できます

もう1つの方法は、特定のシェルを明示的に呼び出すことです。これは、特定のシェルを必要とするスクリプトを実行する場合に特に便利です。したがって、次のことができます。

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

代わりにos.chdir()を使用してください。

既存の回答に記載されている問題は別として、ここでShell=True、またはsubprocess.call()を使用してディレクトリを変更することは好ましくありません。

Pythonには、os.chdir()でディレクトリを変更する独自の方法があります(import osを忘れないでください)。 ~( "home")はいくつかの方法で定義できます。 os.environ["HOME"]

Shell=Trueよりも好まれる理由はa.oと読むことができます。 ここ

3
Jacob Vlijm