Solución:
Soltar shell=True
. Los argumentos para Popen()
se tratan de manera diferente en Unix si shell=True
:
import sys
from subprocess import Popen, PIPE
# populate list of arguments
args = ["mytool.py"]
for opt, optname in zip("-a -x -p".split(), "address port pass".split()):
args.extend([opt, str(servers[server][optname])])
args.extend("some additional command".split())
# run script
p = Popen([sys.executable or 'python'] + args, stdout=PIPE)
# use p.stdout here...
p.stdout.close()
p.wait()
Tenga en cuenta que pasando shell=True
para comandos con entrada externa es un peligro para la seguridad, como se describe en una advertencia en los documentos.
Cuando usted llama subprocess.Popen
puede pasar una cadena o una lista para que se ejecute el comando. Si pasa una lista, los elementos deben dividirse de una manera particular.
En su caso, debe dividirlo de la siguiente manera:
command = ["python", "mytool.py", "-a", servers[server]['address'],
"-x", servers[server]['port'],
"-p", servers[server]['pass'],
"some", "additional", "command"]
p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
Esto se debe a que si pasa en una lista, Popen
asume que ya ha dividido la línea de comando en palabras (los valores que terminarían en sys.argv
), por lo que no es necesario.
De la forma en que lo está llamando, intentará ejecutar un binario llamado “python mytool.py -a”, que no es lo que quería decir.
La otra forma de solucionarlo es unir todas las palabras en una cadena (que Popen
luego se separará – ver subprocess.list2cmdline
). Pero es mejor que use la versión de lista si es posible: brinda un control más simple de cómo se divide la línea de comandos (si los argumentos tienen espacios o comillas, por ejemplo) sin tener que perder el tiempo con comillas.
Tu problema de tipo str
por Primero Popen
argumento. Reemplácelo por list
. El siguiente código puede funcionar:
address = servers[server]['address']
port = servers[server]['port']
pass = servers[server]['pass']
command = "python mytool.py -a %s -x %d -p %s some additional command" % (address, port, pass)
p = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
# it is a list^^^^^^^^^^^^^^^ shell=False
Si command
los argumentos se obtienen de una fuente confiable que puede construir command
y úsalo con shell=True
de tal manera:
import pipes as p
command = "python mytool.py -a {} -x {} -p {} some additional command".format(p.quote(address), p.quote(port), p.quote(pass))
p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
Nota 1: construido command
con shell=True
es potencialmente inseguro. Usar pipes.quote()
para reducir la posibilidad de inyección.
Nota 2: pipes.quote()
obsoleto desde python2
; por python3
usar shlex
módulo.