python subprocess.call () não está funcionando como esperado

8

Eu comecei neste buraco de coelho como um meio de me familiarizar com a forma como alguém iria criar um script de configuração em python. A escolha do python foi simplesmente baseada na minha familiaridade com ele, embora eu tenha certeza de que haveria alternativas melhores que o python para essa tarefa.

O objetivo desse script era instalar o ROS na máquina que está executando o script e também configurar o ambiente catkin. Instruções podem ser encontradas aqui e aqui , respectivamente.

O script como está atualmente é o seguinte:

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

Quando o script é executado atualmente, ele comete erros:

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

Verifiquei que o comando funciona corretamente quando executado manualmente a partir de uma janela de terminal e, como tal, acredito que isso seja um mal-entendido fundamental sobre como esse script e seu escopo são tratados no sistema operacional. A parte que está me causando muita confusão é por que ele reclama que não é possível localizar o diretório fornecido, enquanto eu verifiquei que esse diretório existe. Quando o comando é impresso a partir do python e colado em uma janela do terminal, nenhum erro é encontrado.

    
por beeedy 22.07.2016 / 01:15

3 respostas

13

Por padrão, subprocess.call não usa um shell para executar nossos comandos, portanto você não pode executar comandos shell como cd .

Para usar um shell para executar seus comandos, use shell=True como parâmetro. Nesse caso, recomenda-se passar seus comandos como uma única string e não como uma lista. E como ele é executado por um shell, você também pode usar ~/ em seu caminho:

subprocess.call("(cd ~/catkin_ws/src && catkin_make)", shell=True)
    
por Florian Diesch 22.07.2016 / 01:31
5

subprocess.call() espera uma lista, com o primeiro item obviamente sendo um comando shell legítimo. Compare isso, por exemplo:

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

No seu caso, subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws/src)"]) esperará encontrar o binário que se pareça com isso (observe a barra invertida que designa o recurso de espaço):

 cd\ /home/user/catkin_ws/src

Isso é tratado como um nome único que deve ser esperado em algum lugar do sistema. O que você realmente gostaria de fazer é:

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

Note que eu removi parênteses em torno da vírgula, já que não há razão para usar subshell.

EDITAR :

Mas já foi mencionado por progo nos comentários que usar cd neste caso é redundante. A resposta de Florian também menciona corretamente que subprocess.call() não usa shell. Você poderia abordar isso de duas maneiras. Um, você poderia usar subprocess.call("command string",shell=True)

A outra maneira é chamar shell específico explicitamente. Isso é especialmente útil se você deseja executar um script que requer um shell específico. Assim você poderia fazer:

subprocess.call(['bash' , os.path.expanduser('~')  + "/catkin_ws/src"  ) ] )
    
por Sergiy Kolodyazhnyy 22.07.2016 / 01:21
3

Use os.chdir() em vez disso.

Além dos problemas mencionados nas respostas existentes, eu não preferiria usar shell=True , nem subprocess.call() aqui para alterar o diretório.

O Python tem sua própria maneira de alterar o diretório em os.chdir() (não se esqueça de import os ). ~ ("home") pode ser definido de várias maneiras, a.o. os.environ["HOME"] .

Razões para preferir que mais de shell=True podem ser lidas a.o. aqui

    
por Jacob Vlijm 22.07.2016 / 11:02