python subprocess.call () не працює, як очікувалося


11

Я запустив цю кролячу нору як засіб, щоб ознайомити себе з тим, як можна було б створити сценарій налаштування в 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

Я переконався, що команда працює правильно, коли виконується вручну з вікна терміналу, і я вважаю, що це фундаментальне непорозуміння щодо того, як цей сценарій та його обсяг обробляються в ОС. Частина, яка викликає у мене велику плутанину, полягає в тому, що вона скаржиться, що не в змозі знайти наданий каталог, в той час як я переконався, що цей каталог існує. Коли команда надрукована з python та вставлена ​​у вікно терміналу, помилок не виникає.


У Python є своєos.chdir()
Яків Влійм

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 використовував оболонку, і не знав, що це потрібно чітко вказати. Вищезгадана команда спрацювала саме так, як і було призначено
1616 року

1
Чому б не використовувати os.chdir()?
Яків Влійм

3
Як щодо subprocess.call(['catkin_make'], cwd=os.path.expanduser('~/catkin_ws/src'))?
Метт Нордхофф

shell=Trueвикличе оболонку за замовчуванням, яка є тире. Якщо сценарій, що в ОП містить башизми, він може зламатися. Я додав редагування до своєї відповіді, альтернативним рішенням буде явний виклик конкретної оболонки. Особливо корисно, якщо хтось має справу зі сценарієм csh
Сергій Колодяжний,

1
Найкраще рішення - пропозиція Метта Нордхоффа. Використання shell=True навіть із фіксованими командами відкриває вразливі місця безпеки (наприклад, вразлива система може спрацьовувати оболонки). Правило: якщо ви не можете користуватися shell=True, слід уникати цього. cwdПараметр там точно робити вигляд виклику ОП хоче.
Бакуріу

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в цьому випадку зайве . Відповідь Флоріана також належним чином зазначає, що subprocess.call()не використовується оболонка. Ви можете підійти до цього двома способами. Один, ви можете використовуватиsubprocess.call("command string",shell=True)

Інший спосіб - явно викликати конкретну оболонку. Це особливо корисно, якщо ви хочете запустити сценарій, який вимагає конкретної оболонки. Таким чином, ви можете зробити:

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

1
call()не очікує законної команди оболонки; він очікує, що знайде шлях до фактичного виконуваного файлу. І виклик автономної роботи cdнічого не досягає: CWD - це певна технологічна змінна, яка перестає існувати, коли процес закінчується.
nperson325681

@progo хороший момент, я був настільки зосереджений на редагуванні команди OP, що навіть не помітив, що cdтут нічого не робитиму. . . . А що стосується "легітимного", то я все-таки є доречним фразуванням, я вважаю - якщо я даю subprocess.call()щось, чого він не може знайти, наприклад ['ls -l'] , це не буде законним
Сергій Колодяжний,

@progo зробив невелику редагування, будь ласка, перегляньте
Сергій Колодяжний

3

Використовуйте os.chdir()замість цього.

Окрім питань, згаданих у існуючих відповідях, я б не віддав перевагу використовувати shell=Trueта не subprocess.call()змінювати каталог.

У Python є власний спосіб зміни каталогу os.chdir()(не забудьте import os). ~( «Будинок») можна визначити кілька способів, ат os.environ["HOME"].

Причини віддавати перевагу цьому shell=Trueможна прочитати тут


0

Зауважте, що використання os.chdir()може спричинити небажані побічні ефекти, наприклад, якщо ви використовуєте багатопотокове читання . subprocessвсі методи надають cwdаргумент ключового слова, який запустить запитуваний підпроцес у цьому каталозі, не впливаючи на інші частини вашого python-процесу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.