python subprocess.call () tidak berfungsi seperti yang diharapkan

11

Saya memulai lubang kelinci ini sebagai sarana untuk membiasakan diri dengan bagaimana orang akan membuat skrip pengaturan dengan python. Pilihan python hanya berakar pada keakraban saya dengan itu sementara saya yakin akan ada alternatif yang lebih baik daripada python untuk tugas ini.

Tujuan skrip ini adalah menginstal ROS ke mesin yang menjalankan skrip dan juga mengatur lingkungan catkin. Arah dapat ditemukan di sini dan di sini , masing-masing.

Script saat ini duduk adalah sebagai berikut:

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

Ketika skrip saat ini berjalan itu kesalahan keluar dengan kesalahan:

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

Saya telah memverifikasi bahwa perintah itu berfungsi dengan benar ketika dieksekusi secara manual dari jendela terminal, dan karena itu saya percaya ini adalah kesalahpahaman mendasar tentang bagaimana skrip ini dan ruang lingkupnya ditangani dalam OS. Bagian yang menyebabkan saya banyak kebingungan adalah mengapa ia mengeluh bahwa ia tidak dapat menemukan direktori yang disediakan, sementara saya telah memverifikasi bahwa direktori ini ada. Ketika perintah agak dicetak dari python dan disisipkan ke jendela terminal tidak ada kesalahan yang ditemui.

cantik
sumber
Python memiliki sendirios.chdir()
Jacob Vlijm
1
Jika Anda menggunakan Python 3, cwdcall
kirimkan

Jawaban:

18

Secara default subprocess.calltidak menggunakan shell untuk menjalankan perintah kami sehingga Anda tidak dapat shell perintah seperti cd.

Untuk menggunakan shell untuk menjalankan perintah Anda gunakan shell=Truesebagai parameter. Dalam hal ini disarankan untuk meneruskan perintah Anda sebagai string tunggal daripada sebagai daftar. Dan karena dijalankan oleh shell yang dapat Anda gunakan ~/di jalur Anda juga:

subprocess.call("(cd ~/catkin_ws/src && catkin_make)", shell=True)
Florian Diesch
sumber
1
Terima kasih! Saya mendapat kesan bahwa subprocess.call menggunakan shell, dan tidak menyadari bahwa itu harus dinyatakan secara eksplisit. Perintah di atas bekerja persis seperti yang dimaksudkan
beeedy
1
Kenapa tidak digunakan os.chdir()?
Jacob Vlijm
3
Bagaimana dengan subprocess.call(['catkin_make'], cwd=os.path.expanduser('~/catkin_ws/src'))?
Matt Nordhoff
shell=Trueakan memanggil shell default, yang merupakan tanda hubung. Jika skrip yang berisi bashisme OP itu mungkin rusak. Saya telah menambahkan edit pada jawaban saya, solusi alternatif adalah dengan secara eksplisit memanggil shell tertentu. Terutama berguna jika seseorang berurusan dengan skrip csh
Sergiy Kolodyazhnyy
1
Solusi terbaik adalah saran Matt Nordhoff. Menggunakan shell=True bahkan dengan perintah tetap membuka kerentanan keamanan (mis. Shellshock dapat dipicu pada sistem yang rentan). Aturan praktis: jika Anda dapat menghindari penggunaan shell=TrueAnda harus menghindarinya. The cwdparameter sana persis melakukan jenis panggilan OP ingin.
Bakuriu
5

subprocess.call() mengharapkan daftar, dengan item pertama jelas merupakan perintah shell yang sah. Bandingkan ini misalnya:

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

Dalam kasus Anda, Anda subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws/src)"])akan menemukan biner yang mirip (perhatikan backslash yang menunjuk ruang karakter):

 cd\ /home/user/catkin_ws/src

Itu diperlakukan sebagai satu nama tunggal yang diharapkan untuk hidup di suatu tempat di sistem Anda. Apa yang benar-benar ingin Anda lakukan adalah:

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

Perhatikan bahwa saya telah menghapus tanda kurung di sekitar koma, karena tidak ada alasan untuk menggunakan subkulit.

EDIT :

Tetapi telah disebutkan oleh progo dalam komentar bahwa menggunakan cddalam kasus ini berlebihan. Jawaban Florian juga menyebutkan dengan benar bahwa subprocess.call()tidak menggunakan shell. Anda bisa mendekati itu dengan dua cara. Satu, Anda bisa menggunakannyasubprocess.call("command string",shell=True)

Cara lain, adalah dengan memanggil shell spesifik secara eksplisit. Ini sangat berguna jika Anda ingin menjalankan skrip yang memerlukan shell tertentu. Dengan demikian Anda dapat melakukan:

subprocess.call(['bash' , os.path.expanduser('~')  + "/catkin_ws/src"  ) ] )
Sergiy Kolodyazhnyy
sumber
1
call()tidak mengharapkan perintah shell yang sah; ia mengharapkan untuk menemukan jalur ke executable yang sebenarnya. Dan memanggil mandiri cdtidak mencapai apa-apa: CWD adalah variabel proses spesifik yang tidak ada lagi setelah proses keluar.
nperson325681
@progo poin bagus, saya sangat fokus pada pengeditan perintah OP sehingga saya bahkan tidak menyadari bahwa cdtidak akan melakukan apa pun di sini. . . . Tetapi untuk "sah", itu masih merupakan ungkapan yang tepat saya percaya - jika saya memberikan subprocess.call()sesuatu yang tidak dapat ditemukan, seperti ['ls -l'] , itu tidak akan sah
Sergiy Kolodyazhnyy
@progo membuat suntingan kecil, silakan tinjau
Sergiy Kolodyazhnyy
3

Gunakan os.chdir()sebagai gantinya.

Terlepas dari masalah, yang disebutkan dalam jawaban yang ada, saya tidak akan suka menggunakan shell=True, atau di subprocess.call()sini untuk mengubah direktori.

Python memiliki caranya sendiri dalam mengubah direktori os.chdir()(jangan lupa import os). ~("rumah") dapat didefinisikan dalam beberapa cara, ao os.environ["HOME"].

Alasan untuk memilih itu shell=Truebisa dibaca di sini

Yakub Vlijm
sumber
0

Perhatikan bahwa menggunakan os.chdir()dapat menyebabkan efek samping yang tidak diinginkan, misalnya jika Anda menggunakan multithreading . subprocessmetode semua memberikan cwdargumen kata kunci yang akan menjalankan subproses yang diminta dalam direktori itu, tanpa mempengaruhi bagian lain dari proses python Anda.

ipetrik
sumber