python sub process.call () không hoạt động như mong đợi


11

Tôi bắt đầu xuống cái hố thỏ này như một phương tiện để làm quen với cách người ta sẽ tạo ra một kịch bản thiết lập trong python. Sự lựa chọn của python chỉ đơn giản bắt nguồn từ sự quen thuộc của tôi với nó trong khi tôi chắc chắn sẽ có những lựa chọn thay thế tốt hơn python cho nhiệm vụ này.

Mục tiêu của tập lệnh này là cài đặt ROS lên máy chạy tập lệnh và cũng thiết lập môi trường catkin. Hướng dẫn có thể được tìm thấy ở đâyở đây , tương ứng.

Kịch bản như hiện tại là như sau:

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

Khi tập lệnh hiện đang chạy, nó bị lỗi với lỗi:

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

Tôi đã xác minh rằng lệnh hoạt động chính xác khi được thực hiện thủ công từ cửa sổ đầu cuối và vì vậy tôi tin rằng đây là một sự hiểu lầm cơ bản về cách tập lệnh này và phạm vi của nó được xử lý trong HĐH. Phần gây ra cho tôi rất nhiều nhầm lẫn là tại sao nó phàn nàn rằng nó không thể xác định vị trí thư mục được cung cấp, trong khi tôi đã xác minh rằng thư mục này tồn tại. Khi lệnh được in từ python và dán vào cửa sổ terminal, không gặp lỗi.


Python có cái riêng của nóos.chdir()
Jacob Vlijm

Nếu bạn đang sử dụng Python 3, chỉ cần chuyển cwdđối số chocall
intsco

Câu trả lời:


18

Theo mặc định subprocess.call, không sử dụng shell để chạy các lệnh của chúng tôi, vì vậy bạn không thể shell các lệnh như thế nào cd.

Để sử dụng shell để chạy các lệnh của bạn, hãy sử dụng shell=Truelàm tham số. Trong trường hợp đó, bạn nên chuyển các lệnh của mình dưới dạng một chuỗi chứ không phải là một danh sách. Và vì nó được chạy bởi một cái vỏ mà bạn cũng có thể sử dụng ~/trên đường dẫn của mình:

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

1
Cảm ơn bạn! Tôi có ấn tượng rằng sub process.call đã sử dụng shell và không biết rằng nó phải được tuyên bố rõ ràng. Lệnh trên hoạt động chính xác như dự định
hãy chú ý

1
Tại sao không sử dụng os.chdir()?
Jacob Vlijm

2
Thế còn subprocess.call(['catkin_make'], cwd=os.path.expanduser('~/catkin_ws/src'))?
Matt Nordhoff

shell=Truesẽ gọi shell mặc định, đó là dấu gạch ngang. Nếu một tập lệnh mà OP chứa bashism, nó có thể bị hỏng. Tôi đã thêm chỉnh sửa vào câu trả lời của mình, giải pháp thay thế sẽ là gọi rõ ràng trình bao cụ thể. Đặc biệt hữu ích nếu ai đó đang xử lý kịch bản csh
Sergiy Kolodyazhnyy

Giải pháp tốt nhất là đề xuất Matt Nordhoff. Việc sử dụng shell=True ngay cả với các lệnh cố định sẽ mở ra các lỗ hổng bảo mật (ví dụ shellshock có thể được kích hoạt trên một hệ thống dễ bị tổn thương). Nguyên tắc nhỏ: nếu bạn có thể tránh sử dụng, shell=Truebạn nên tránh nó. Các cwdtham số là có chính xác để làm các loại cuộc gọi OP muốn.
Bakuriu

5

subprocess.call() mong đợi một danh sách, với mục đầu tiên rõ ràng là một lệnh shell hợp pháp. So sánh điều này chẳng hạn:

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

Trong trường hợp của bạn, subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws/src)"])bạn sẽ mong muốn tìm thấy nhị phân trông giống như vậy (lưu ý dấu gạch chéo ngược chỉ định không gian charater):

 cd\ /home/user/catkin_ws/src

Điều đó được coi là một tên duy nhất dự kiến ​​sẽ sống ở đâu đó trên hệ thống của bạn. Những gì bạn thực sự muốn làm là:

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

Lưu ý rằng tôi đã xóa dấu ngoặc đơn xung quanh dấu phẩy, vì không có lý do để sử dụng subshell.

CHỈNH SỬA :

Nhưng nó đã được đề cập bởi progo trong các ý kiến ​​rằng sử dụng cdtrong trường hợp này là dư thừa. Câu trả lời của Florian cũng đề cập chính xác rằng subprocess.call()không sử dụng vỏ. Bạn có thể tiếp cận điều đó theo hai cách. Một, bạn có thể sử dụngsubprocess.call("command string",shell=True)

Một cách khác, là gọi vỏ cụ thể một cách rõ ràng. Điều này đặc biệt hữu ích nếu bạn muốn chạy một tập lệnh yêu cầu shell cụ thể. Do đó bạn có thể làm:

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

1
call()không mong đợi một lệnh shell hợp pháp; nó hy vọng sẽ tìm thấy một đường dẫn đến một thực thi thực tế. Và việc gọi một độc lập cdkhông đạt được bất cứ điều gì: CWD là một biến số cụ thể của quá trình không còn tồn tại khi quá trình thoát ra.
nperson325681

@progo điểm tốt, tôi đã rất tập trung vào việc chỉnh sửa lệnh của OP đến nỗi tôi thậm chí không nhận thấy rằng cdsẽ không làm gì ở đây. . . . Nhưng đối với "hợp pháp", tôi vẫn tin rằng đó là cụm từ phù hợp - nếu tôi đưa ra subprocess.call()thứ gì đó không thể tìm thấy, ['ls -l'] thì nó sẽ không hợp pháp
Sergiy Kolodyazhnyy

@progo đã thực hiện một chỉnh sửa nhỏ, vui lòng xem lại
Sergiy Kolodyazhnyy

3

Sử dụng os.chdir()thay thế.

Ngoài các vấn đề, được đề cập trong các câu trả lời hiện có, tôi không thích sử dụng shell=True, cũng không phải subprocess.call()ở đây để thay đổi thư mục.

Python có cách thay đổi thư mục riêng trong os.chdir()(đừng quên import os). ~("nhà") có thể được định nghĩa theo nhiều cách, áo os.environ["HOME"].

Những lý do để thích hơn shell=Truecó thể được đọc áo ở đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.