2011-04-14 14 views
37

Estoy confundido acerca de cómo subprocess busca el ejecutable cuando usa Popen(). Funciona si se le dan rutas absolutas al proceso secundario, pero estoy tratando de usar rutas relativas. He encontrado que si configuro la variable de entorno PYTHONPATH, entonces puedo obtener módulos importados de esa ruta, y PYTHONPATH está ahí en sys.path, pero no parece ayudar con el comportamiento de subprocess.Popen. También he intentado editar el archivo PYTHONPATH añadiendo sitecustomize.py a os.environ, al igual quepython subprocess Popen environment PATH?

# copy PYTHONPATH environment variable into PATH to allow our stuff to use 
# relative paths for subprocess spawning 
import os 
if os.getenv('PYTHONPATH') is not None and os.getenv('PATH') is not none: 
    os.environ['PATH'] = ':'.join([os.getenv('PATH'), os.getenv('PYTHONPATH')]) 

y verificó que al poner en marcha pitón, de forma interactiva, con ipython, o ejecutando un script desde la línea de comandos, que es PYTHONPATH aparece con éxito en os.environ. Sin embargo, subrocess.Popentodavía no busca allí el ejecutable. Pensé que se suponía que debía heredar el entorno de los padres, si no se especifica env kwarg? Luego traté de dar env explícitamente, primero haciendo una copia de os.getenv y en segundo lugar simplemente dando env={'PATH': '/explicit/path/to/search/from'}, y todavía no encuentra el ejecutable. Ahora estoy perplejo.

suerte Un ejemplo ayudará a explicar mi problema con mayor claridad:

/dir/subdir1/some_executable
/dir/subdir2/some_script.py

# some_script.py 
from subprocess import Popen, PIPE 
spam, eggs = Popen(['../subdir1/some_executable'], stdout=PIPE, stderr=PIPE).communicate() 

Si estoy en /dir/subdir2 y yo ejecutar python some_script.py funciona, pero si estoy en /dir y ejecuto python subdir2/some_script.py aunque /dir/subdir2 se encuentre en el , entonces el subproceso arrojará OSError: [Errno 2] No such file or directory.

+1

Al volver a leer la pregunta, creo que veo el problema. En un shell de comandos, cambie a '/ dir' y vea qué sucede si escribe' ../ subdir1/some_executable'. – ncoghlan

+0

ok veo lo que estás diciendo, mi malentendido fue la suposición de que las rutas relativas serían buscadas de la misma manera que una llamada a un programa simple. gracias – wim

Respuesta

45

(rellenar en detalle a partir de un comentario que hacer una respuesta por separado)

En primer lugar, las rutas relativas (rutas que contienen barras) nunca se comprueban en cualquier camino, no importa lo que haces. Son relativos al directorio de trabajo actual solamente. Si necesita resolver rutas relativas, tendrá que buscar la RUTA manualmente o enviar la ruta para incluir los subdirectorios y luego simplemente usar el nombre del comando como en mi sugerencia a continuación.

Si desea ejecutar un programa relativa a la ubicación de la secuencia de comandos de Python, utilice __file__ e ir de allí para encontrar la ruta absoluta del programa, y ​​luego usar la ruta absoluta en Popen.

En segundo lugar, hay an issue in the Python bug tracker acerca de cómo Python se ocupa de los comandos desnudos (sin barras). Básicamente, en Unix/Mac Popen utiliza os.execvp cuando se invoca con shell=False, lo que significa que se ve en el valor de PATHcomo lo fue cuando lanzó Python y ninguna cantidad de cambio de os.environ ayudará a solucionar eso. Además, en Windows con shell=False, no presta atención a PATH en absoluto, y solo buscará en relación con el directorio de trabajo actual.

Si sólo necesita la evaluación ruta y realmente no desea ejecutar la línea de comandos a través de una concha, y está en UNIX, aconsejo el uso de env en lugar de shell=True, como en Popen(['/usr/bin/env', 'progtorun', other, args], ...). Esto le permite pasar una RUTA diferente al proceso env, que lo usará para encontrar el programa. También evita problemas con metacaracteres del shell y posibles problemas de seguridad al pasar argumentos a través del shell. Obviamente, en Windows (casi la única plataforma sin un /usr/bin/env) tendrá que hacer algo diferente.

+1

explicación muy clara y útil, gracias – wim

+3

+1 "Además, en Windows con shell = False, no presta atención a PATH en absoluto, y solo se verá en relación con el directorio de trabajo actual." Solo me ayudó con un gran problema, ¡gracias! –

+1

Una forma simple que debería funcionar en Windows también es dar explícitamente 'os.environ ['PATH']' como el argumento 'env' a' subprocess.Popen', como se hace aquí: http://stackoverflow.com/a/4453495/1959808 y allí: http://stackoverflow.com/a/20669704/1959808. –

0

pythonpath se establece en la ruta desde la que se ejecuta el intérprete de python. Entonces, en el segundo caso de su ejemplo, la ruta se establece en/dir y no/dir/subdir2 Es por eso que obtiene un error.

+1

no creo que esto sea correcto, porque si escribo un script simple para imprimir os.environ, entonces PYTHONPATH es el mismo sin importar desde donde ejecuto el intérprete. PYTHONPATH se configuró en/etc/environment y se usa para aumentar la ruta de búsqueda de los módulos – wim

+0

. Quería decir que el directorio desde el que se ejecuta python, ese directorio se agrega a pythonpath. Aquí en el segundo caso, se agrega/dir, y no/dir/subdir2. Entonces, puede cambiar su código para reflejar los cambios (una forma puede ser agregar/dir/subdir2 a os.path en su código) o ejecutar python desde el directorio apropiado. – c0da

8

Parece que está un poco confundido acerca de la naturaleza de PATH y PYTHONPATH.

PATH es una variable de entorno que le dice al shell del sistema operativo dónde buscar ejecutables.

PYTHONPATH es una variable de entorno que le dice al intérprete de Python dónde buscar módulos para importar. No tiene nada que ver con subprocess encontrar archivos ejecutables.

Debido a las diferencias en la implementación subyacente, subprocess.Popen solo buscará la ruta por defecto en sistemas que no sean Windows (Windows tiene algunos directorios del sistema siempre busca, pero eso es distinto del procesamiento PATH). La única forma de cruz-plataforma fiable para escanear el camino está pasando shell=True a la llamada sub-proceso, pero que tiene sus propios problemas (como se detalla en el Popen documentation)

Sin embargo, parece que su principal problema es que usted está pasando una fragmento de ruta al Popen en lugar de un nombre de archivo simple. Tan pronto como tenga un separador de directorio, deshabilitará la búsqueda PATH, incluso en una plataforma que no sea de Windows (por ejemplo, consulte la documentación de Linux para exec family of functions).

+2

Esto no coincide con los documentos de Python. [Los documentos de Popen] (http://docs.python.org/library/subprocess.html) indican que el programa se ejecuta a través de 'os.execvp', y que la llamada SÍ tiene en cuenta la variable de entorno PATH. Además, si SOLO necesita evaluación de ruta, le aconsejo utilizar 'env' en lugar de' shell = True', como en 'Popen (['/ usr/bin/env', 'progtorun', other, args], ...) '. Esto evita problemas con metacaracteres del shell y posibles problemas de seguridad al pasar argumentos a través del shell. –

+1

Aunque son * NIX específicos, no funcionan en Windows, por lo que no me gusta recomendarlos como soluciones para un módulo nominalmente multiplataforma. Estás en lo cierto de que mi respuesta es incorrecta, aunque escrita, editará en consecuencia. – ncoghlan

+2

Se ha actualizado para dejar en claro que no buscar PATH de forma predeterminada es solo algo de Windows, sino también para señalar el problema real (un separador de directorio en el comando que se ejecutará). – ncoghlan

2

Una ruta relativa en el subproceso.Popen actúa en relación con el directorio de trabajo actual, no con los elementos de los sistemas PATH. Si ejecuta python subdir2/some_script.py desde /dir, la ubicación ejecutable esperada será /dir/../subdir2/some_executable, a.k.a /subdir2/some_executable.

Si definitivamente desea utilizar rutas de acceso relativas desde un directorio propio de scripts a un ejecutable en particular, la mejor opción sería construir primero una ruta absoluta desde la parte de directorio de la variable global __file__.

#/usr/bin/env python 
from subprocess import Popen, PIPE 
from os.path import abspath, dirname, join 
path = abspath(join(dirname(__file__), '../subdir1/some_executable')) 
spam, eggs = Popen(path, stdout=PIPE, stderr=PIPE).communicate() 
Cuestiones relacionadas