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


11

私は、Pythonでセットアップスクリプトを作成する方法に慣れるために、このうさぎの穴を掘り下げました。Pythonの選択は、私がそれに精通していることに根ざしています。このタスクには、Pythonよりも優れた代替策があると確信しています。

このスクリプトの目的は、スクリプトを実行しているマシンにROSをインストールし、catkin環境をセットアップすることです。道順はここここでそれぞれ見つけることができます。

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

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から出力され、ターミナルウィンドウに貼り付けられた場合、エラーは発生しません。


Pythonには独自の機能os.chdir()
Jacob Vlijm 2016

1
Python 3を使用している場合は、cwd引数を渡すだけですcall
intsco

回答:


18

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

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

subprocess.call("(cd ~/catkin_ws/src && catkin_make)", shell=True)

1
ありがとうございました!私はsubprocess.callがシェルを使用しているという印象を受け、それを明示的に記述する必要があることを知りませんでした。上記のコマンドは意図したとおりに機能しました
beeedy

1
なぜ使用しないのos.chdir()ですか?
Jacob Vlijm 2016

3
いかがsubprocess.call(['catkin_make'], cwd=os.path.expanduser('~/catkin_ws/src'))ですか?
Matt Nordhoff

shell=Trueデフォルトのシェル、つまりダッシュを呼び出します。OPにbashismが含まれているスクリプトは壊れる可能性があります。私は私の回答に編集を追加しました。別の解決策は、特定のシェルを明示的に呼び出すことです。誰かがcshスクリプトを扱っている場合に特に便利です
セルギーコロディアズニー

1
最善の解決策は、Matt Nordhoffの提案です。固定コマンドでshell=True 使用すると、セキュリティの脆弱性が開きます(たとえば、脆弱なシステムでシェルショックがトリガーされる可能性があります)。親指のルール:あなたが使用して避けることができれば、shell=Trueあなたがすべきことを避けます。cwdパラメータは、OPが望んでいるコールの種類を行うには、正確にそこにあります。
Bakuriu

5

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はすでに言及しています。フロリアンの答えはまた、subprocess.call()シェルを使用しないことを適切に述べています。これには2つの方法でアプローチできます。1つは、使用できますsubprocess.call("command string",shell=True)

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

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

1
call()正当なシェルコマンドを想定していません。実際の実行可能ファイルへのパスが見つかることを期待しています。また、スタンドアロンを呼び出してcdも何も起こりません。CWDはプロセス固有の変数であり、プロセスが終了すると存在しなくなります。
nperson325681 2016

@progoの良い点、私はOPのコマンドを編集することに集中していたので、cdここでは何もしないことに気づきませんでした。。。。しかし、「正当な」については、それでも適切な表現であると私は信じてsubprocess.call()います。['ls -l']
たとえば

@progoが少し編集しました。確認してください
Sergiy Kolodyazhnyy

3

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

既存の回答で述べられている問題は別として、私はを使用shell=Trueしたり、subprocess.call()ここでディレクトリを変更したりしません。

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

それを好む理由はこちらからshell=True読むことができます


0

マルチスレッドを使用しているos.chdir()場合など、使用すると意図しない副作用が発生する可能性があることに注意してください。メソッドはすべて、Pythonプロセスの他の部分に影響を与えることなく、そのディレクトリで要求されたサブプロセスを実行するキーワード引数を提供します。subprocesscwd

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.