example español check_output python unix popen

español - subprocess.check_output example python



Llamar al comando "origen" desde el subproceso.Popen (7)

Tengo un script .sh al que llamo con la source the_script.sh . Llamar esto regularmente está bien. Sin embargo, estoy tratando de llamarlo desde mi script python, a través del subprocess.Popen . subprocess.Popen .

Al llamarlo desde Popen, recibo los siguientes errores en las siguientes dos llamadas al escenario:

foo = subprocess.Popen("source the_script.sh") Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/python2.7/subprocess.py", line 672, in __init__ errread, errwrite) File "/usr/lib/python2.7/subprocess.py", line 1213, in _execute_child raise child_exception OSError: [Errno 2] No such file or directory >>> foo = subprocess.Popen("source the_script.sh", shell = True) >>> /bin/sh: source: not found

¿Lo que da? ¿Por qué no puedo llamar "fuente" desde Popen, cuando puedo fuera de Python?


Broken Popen("source the_script.sh") es equivalente a Popen(["source the_script.sh"]) que intenta sin éxito ''source the_script.sh'' programa ''source the_script.sh'' . No puede encontrarlo, de ahí el error "No such file or directory" .

Broken Popen("source the_script.sh", shell=True) falla porque source es un comando bash incorporado (tipo help source en bash) pero el shell por defecto es /bin/sh que no lo entiende ( /bin/sh usa . ). Asumiendo que podría haber otro bash-ismo en the_script.sh , debería ejecutarse usando bash:

foo = Popen("source the_script.sh", shell=True, executable="/bin/bash")

Como dijo @IfLoop , no es muy útil ejecutar la source en un subproceso porque no puede afectar el entorno de los padres.

os.environ.update(env) métodos basados ​​en os.environ.update(env) fallan si the_script.sh ejecuta unset para algunas variables. os.environ.clear() podría llamar a os.environ.clear() para restablecer el entorno:

#!/usr/bin/env python import os from pprint import pprint from subprocess import check_output os.environ[''a''] = ''a''*100 # POSIX: name shall not contain ''='', value doesn''t contain ''/0'' output = check_output("source the_script.sh; env -0", shell=True, executable="/bin/bash") # replace env os.environ.clear() os.environ.update(line.partition(''='')[::2] for line in output.split(''/0'')) pprint(dict(os.environ)) #NOTE: only `export`ed envvars here

Utiliza env -0 y .split(''/0'') sugeridos por @unutbu

Para admitir bytes arbitrarios en os.environb , se puede usar el módulo json (suponiendo que usemos la versión de Python donde el problema "json.dumps no analiza por json.loads" es fijo):

Para evitar pasar el entorno a través de las tuberías, el código de Python podría cambiarse para invocarse en el entorno de subproceso, por ejemplo:

#!/usr/bin/env python import os import sys from pipes import quote from pprint import pprint if "--child" in sys.argv: # executed in the child environment pprint(dict(os.environ)) else: python, script = quote(sys.executable), quote(sys.argv[0]) os.execl("/bin/bash", "/bin/bash", "-c", "source the_script.sh; %s %s --child" % (python, script))


Parece que hay muchas respuestas a esto, no las he leído todas, así que tal vez ya lo hayan señalado; pero, al invocar comandos de shell como este, debe pasar shell = True a la llamada de Popen. De lo contrario, puede llamar a Popen (shlex.split ()). asegúrese de importar shlex.

De hecho, uso esta función para obtener un archivo y modificar el entorno actual.

def set_env(env_file): while True: source_file = ''/tmp/regr.source.%d''%random.randint(0, (2**32)-1) if not os.path.isfile(source_file): break with open(source_file, ''w'') as src_file: src_file.write(''#!/bin/bash/n'') src_file.write(''source %s/n''%env_file) src_file.write(''env/n'') os.chmod(source_file, 0755) p = subprocess.Popen(source_file, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (out, err) = p.communicate() setting = re.compile(''^(?P<setting>[^=]*)='') value = re.compile(''=(?P<value>.*$)'') env_dict = {} for line in out.splitlines(): if setting.search(line) and value.search(line): env_dict[setting.search(line).group(''setting'')] = value.search(line).group(''value'') for k, v in env_dict.items(): os.environ[k] = v for k, v in env_dict.items(): try: assert(os.getenv(k) == v) except AssertionError: raise Exception(''Unable to modify environment'')


Si desea aplicar el comando de origen a otros scripts o ejecutables, entonces puede crear otro archivo de script de ajuste y llamar al comando "origen" desde allí con cualquier lógica adicional que necesite. En ese caso, este comando fuente modificará el contexto local donde se está ejecutando, es decir, en el subproceso que crea el subproceso. Popen.

Esto no funcionará si necesita modificar el contexto de Python, donde se está ejecutando su programa.


Simplemente podría ejecutar el comando en una subshell y usar los resultados para actualizar el entorno actual.

def shell_source(script): """Sometime you want to emulate the action of "source" in bash, settings some environment variables. Here is a way to do it.""" import subprocess, os pipe = subprocess.Popen(". %s; env" % script, stdout=subprocess.PIPE, shell=True) output = pipe.communicate()[0] env = dict((line.split("=", 1) for line in output.splitlines())) os.environ.update(env)


Una variación en la respuesta de @xApple ya que a veces es útil poder generar un script de shell (en lugar de un archivo de Python) para establecer variables de entorno y quizás realizar otras operaciones de shell, y luego propagar ese entorno al intérprete de Python que perder esa información cuando se cierra la subshell.

La razón de una variación es que la suposición de un formato de salida de "variable" de una variable por línea no es 100% robusto: simplemente tuve que lidiar con una variable (una función de shell, creo) que contenía una nueva línea, que arruinó ese análisis. Así que aquí hay una versión un poco más compleja, que usa Python para formatear el diccionario de entorno de una manera robusta:

import subprocess pipe = subprocess.Popen(". ./shellscript.sh; python -c ''import os; print /"newenv = %r/" % os.environ''", stdout=subprocess.PIPE, shell=True) exec(pipe.communicate()[0]) os.environ.update(newenv)

Tal vez hay una manera más ordenada? Esto también asegura que el análisis del entorno no se estropee si alguien pone una declaración de eco en el script que se está obteniendo. Por supuesto, hay un ejecutivo aquí, así que tenga cuidado con las entradas no confiables ... pero creo que eso está implícito en una discusión sobre cómo generar / ejecutar un script de shell arbitrario ;-)

ACTUALIZACIÓN: vea el comentario de @ unutbu en la respuesta @xApple para una forma alternativa (probablemente más agradable) de manejar nuevas líneas en la salida de env .


source es un shell específico de bash incorporado (y los shells no interactivos a menudo son shells livianos en lugar de bash). En cambio, simplemente llame /bin/sh :

foo = subprocess.Popen(["/bin/sh", "the_script.sh"])


source no es un comando ejecutable, es un shell incorporado.

El caso más común para usar source es ejecutar un script de shell que cambie el entorno y retenga ese entorno en el shell actual. Así es exactamente como funciona virtualenv para modificar el entorno de python predeterminado.

La creación de un subproceso y el uso de la source en el subproceso probablemente no sirva para nada, no modificará el entorno del proceso principal, no se producirá ninguno de los efectos secundarios del uso del script de origen.

Python tiene un comando análogo, execfile , que ejecuta el archivo especificado utilizando el espacio de nombre global actual de python (u otro, si proporciona uno), que podría usar de forma similar a la source comando bash.