Python 将变量传递给Subprocess.Popen

Python 将变量传递给Subprocess.Popen,python,subprocess,Python,Subprocess,我有一个脚本,它通过subprocess.Popen模块调用另一个python脚本。但由于我在变量中存储了参数 我无法执行命令 p = subprocess.Popen(["python mytool.py -a ", servers[server]['address'], "-x", servers[server]['port'], "-p", servers[server]['pass'], "some additional command"], shell=True, stdout=sub

我有一个脚本,它通过subprocess.Popen模块调用另一个python脚本。但由于我在变量中存储了参数

我无法执行命令

p = subprocess.Popen(["python mytool.py -a ", servers[server]['address'], "-x", servers[server]['port'], "-p", servers[server]['pass'], "some additional command"], shell=True, stdout=subprocess.PIPE)

调用
subprocess.Popen
时,可以传递要运行的命令的字符串或列表。如果传递列表,则应以特定方式拆分项目

在您的情况下,您需要像这样拆分它:

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)
这是因为,如果您传入一个列表,
Popen
假定您已经将命令行拆分为单词(这些值最终将出现在
sys.argv
中),因此不需要这样做

您调用它的方式是,它将尝试运行一个名为“python mytool.py-a”的二进制文件,这不是您的意思


另一种修复方法是将所有单词连接成一个字符串(然后
Popen
将拆分该字符串-请参阅)。但如果可能的话,最好使用列表版本-它可以更简单地控制命令行的拆分方式(例如,如果参数中有空格或引号),而不必乱加引号。

您应该将命令连接到整个字符串:

p = subprocess.Popen("python mytool.py -a " + servers[server]['address'] + " -x " + servers[server]['port'] + " -p " + servers[server]['pass'] + " some additional command", shell=True, stdout=subprocess.PIPE)

第一个
Popen
参数的类型
str
中存在问题。将其替换为
列表
。下面的代码可以工作:

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
如果
command
参数来自可靠来源,则可以构造
command
,并将其与
shell=True
一起使用,如下所示:

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)
注1:使用
shell=True构建的
命令
可能不安全。使用
pipes.quote()
减少注射可能性。

注2
pipes.quote()
python2
以来已弃用;对于
python3
使用
shlex
模块。

Drop
shell=True


请注意,对于带有外部输入的命令,传递
shell=True
是一种安全隐患,如警告所述。

使用变量构建命令字符串,或将其作为参数列表传递。答案不应编辑为问题。这样做可以避免整个基于社区加权投票的排序问题,这在这里尤其危险,因为您使用的解决方案存在严重的安全漏洞(如果服务器名称包含
$(rm-rf~)
,猜猜会发生什么?)。另请参见。我已将输出保存到变量,但没有正在执行的脚本的输出或跟踪。可能是直接的错误输出来检查它?添加“stderr=subprocess.PIPE”也如果有人向您提供包含
$(rm-rf~)
的地址列表,您最好希望没有人使用这种基于串联的方法。这是非常不安全的;即使您确实想使用
shell=True
,唯一安全的方法是将变量从脚本代码带外传递:
p=subprocess.Popen(['python mytool.py-a“$1”-x“$2”-p“$3”一些附加命令',''''.',str str(服务器[server]['address'])、str(服务器[server]['port'])、str服务器[server]['pass'])],shell=True,stdout=subprocess.PIPE)
我用来存储输出的变量,但在打印时它是空的(我猜它还没有被执行)。我想你的意思是当你从p.stdout读取时,没有输出?这将是因为命令没有运行。实际上,shell=True可能是在搅浑这里的水-除非您正在使用globbing(例如,展开文件列表),最好将其关闭。如果没有shell=True,脚本将拒绝执行,它将显示“sh:mytool.sh command not found”并将其放在mytool.sh的完整路径中-无论什么“which mytool.sh”在命令行上键入时给出。问题与以前类似,但我现在解决了。谢谢:)如果您传递一个带有
shell=True
的列表,第一个参数将被视为shell脚本,后续参数将被视为该脚本的参数(其
$0
$1
,等等)。此
shlex.split()
操作的结果并不是要以这种方式解析的列表;实际上,它将运行
python
,没有参数。与前半部分相比有了很大改进。Re:后半部分,最好演示
pipes.quote()
(或在python 3中
shlex.quote()
)为了创建可以安全地替换到shell命令中的内容,而不仅仅是输入带有大量警告的不安全代码。@CharlesDuffy,现在更新了。我无法检查上次更新。请查看,谢谢。
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)
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()