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

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

beeedy
источник
Питон имеет свой собственныйos.chdir()
Джейкоб Vlijm
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()?
Джейкоб Влейм
3
Как насчет subprocess.call(['catkin_make'], cwd=os.path.expanduser('~/catkin_ws/src'))?
Мэтт Нордхофф
shell=Trueвызовет оболочку по умолчанию, которая является тире. Если скрипт, который OP содержит bashisms, он может сломаться. Я добавил правку в свой ответ, альтернативным решением будет явный вызов определенной оболочки. Особенно полезно, если кто-то имеет дело с 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"])

Обратите внимание, что я удалил круглые скобки вокруг запятой, так как нет причины использовать subshell.

РЕДАКТИРОВАТЬ :

Но Progo уже упоминал в комментариях, что использование cdв этом случае является излишним. В ответе Флориана также упоминается, что subprocess.call()не используется оболочка. Вы можете подойти к этому двумя способами. Один, вы могли бы использоватьsubprocess.call("command string",shell=True)

Другой способ - явно вызывать конкретную оболочку. Это особенно полезно, если вы хотите запустить скрипт, который требует определенной оболочки. Таким образом, вы можете сделать:

subprocess.call(['bash' , os.path.expanduser('~')  + "/catkin_ws/src"  ) ] )
Сергей Колодяжный
источник
1
call()не ожидает законной команды оболочки; он ожидает найти путь к реальному исполняемому файлу. А вызов автономного cdничего не дает: CWD - это переменная, специфичная для процесса, которая перестает существовать после выхода из процесса.
nperson325681
@ Прошу хорошо, я был так сосредоточен на редактировании команды 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.

ipetrik
источник