Comencé por este agujero del conejo como un medio para familiarizarme con cómo se crearía un script de configuración en Python. La elección de Python simplemente se basó en mi familiaridad con él, mientras que estoy seguro de que habría mejores alternativas que Python para esta tarea.
El objetivo de este script era instalar ROS en la máquina que ejecuta el script y también configurar el entorno catkin. Las instrucciones se pueden encontrar aquí y aquí , respectivamente.
El script tal como está actualmente es el siguiente:
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"])
Cuando la secuencia de comandos se ejecuta actualmente, se produce un error con el error:
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
He verificado que el comando funciona correctamente cuando se ejecuta manualmente desde una ventana de terminal y, como tal, creo que este es un malentendido fundamental sobre cómo se maneja este script y su alcance dentro del sistema operativo. La parte que me está causando mucha confusión es por qué se queja de que no puede localizar el directorio proporcionado, mientras que he verificado que este directorio existe. Cuando el comando se imprime desde Python y se pega en una ventana de terminal, no se encuentran errores.
fuente
os.chdir()
cwd
argumento acall
Respuestas:
Por defecto
subprocess.call
, no utiliza un shell para ejecutar nuestros comandos, por lo que no puede ejecutar comandos comocd
.Para usar un shell para ejecutar sus comandos, use
shell=True
como parámetro. En ese caso, se recomienda pasar los comandos como una sola cadena en lugar de como una lista. Y como lo ejecuta un shell, también puede usarlo~/
en su camino:fuente
os.chdir()
?subprocess.call(['catkin_make'], cwd=os.path.expanduser('~/catkin_ws/src'))
?shell=True
llamará al shell predeterminado, que es guión. Si un script que OP contiene bashismos, puede romperse. He agregado editar a mi respuesta, la solución alternativa sería llamar explícitamente a un shell específico. Especialmente útil si alguien está tratando con un script cshshell=True
incluso con comandos fijos abre vulnerabilidades de seguridad (por ejemplo, el shellshock podría activarse en un sistema vulnerable). La regla general: si puede evitar el usoshell=True
, debe evitarlo. Elcwd
parámetro está ahí exactamente para hacer el tipo de llamada que quiere el OP.subprocess.call()
espera una lista, con el primer elemento obviamente siendo un comando de shell legítimo. Compare esto por ejemplo:En su caso,
subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws/src)"])
esperará encontrar binarios que se vean así (observe la barra diagonal inversa que designa el carácter espacial):Eso se trata como un nombre único que se espera que viva en algún lugar de su sistema. Lo que realmente quieres hacer es:
Tenga en cuenta que he eliminado el paréntesis alrededor de la coma, ya que no hay razón para usar subshell.
EDITAR :
Pero ya ha sido mencionado por progo en los comentarios que usar
cd
en este caso es redundante. La respuesta de Florian también menciona correctamente quesubprocess.call()
no usa shell. Podrías abordar eso de dos maneras. Uno, podrías usarsubprocess.call("command string",shell=True)
La otra forma es llamar explícitamente a un shell específico. Esto es especialmente útil si desea ejecutar un script que requiere un shell específico. Así podrías hacer:
fuente
call()
no espera un comando de shell legítimo; espera encontrar una ruta a un ejecutable real. Y llamar a un independientecd
no logra nada: el CWD es una variable específica del proceso que deja de existir una vez que el proceso finaliza.cd
no haría nada aquí. . . . Pero en cuanto a "legítimo", creo que es una fraseología apropiada: si doysubprocess.call()
algo que no puede encontrar, como['ls -l']
, no será legítimoUsar en su
os.chdir()
lugar.Aparte de los problemas, mencionados en las respuestas existentes, no preferiría usar
shell=True
, nisubprocess.call()
aquí, para cambiar el directorio.Python tiene su propia forma de cambiar el directorio
os.chdir()
(no se olvideimport os
).~
("hogar") se puede definir de varias maneras, entre otrasos.environ["HOME"]
.Las razones para preferir eso
shell=True
se pueden leer aquí.fuente
Tenga en cuenta que el uso
os.chdir()
puede causar efectos secundarios no deseados, por ejemplo, si está usando subprocesos múltiples .subprocess
Todos los métodos proporcionan uncwd
argumento de palabra clave que ejecutará el subproceso solicitado en ese directorio, sin afectar otras partes de su proceso de Python.fuente