为什么尽管传递了参数列表,但将变量传递给 subprocess.Popen 却不起作用?
- 2024-12-17 08:30:00
- admin 原创
- 153
问题描述:
我有一个脚本通过 调用另一个 Python 脚本subprocess.Popen
。但由于我将参数存储在变量中
servers[server]['address']
servers[server]['port']
servers[server]['pass']
我无法执行命令
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
)
解决方案 1:
删除shell=True
。在 Unix 上,如果 的参数Popen()
被区别对待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()
请注意,传递带有外部输入的命令存在安全隐患,如文档中的shell=True
警告所述。
解决方案 2:
调用时,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
然后将其拆分 - 请参阅subprocess.list2cmdline
)。但如果可能,最好使用列表版本 - 它可以更简单地控制命令行的拆分方式(例如,如果参数中有空格或引号),而无需费心引用引号字符。
解决方案 3:
您的问题在于str
第一个Popen
参数的类型。将其替换为list
。以下代码可以工作:
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:command
用构造shell=True
可能不安全。pipes.quote()
用于减少注入可能性。
注 2:pipes.quote()
自 起已弃用python2
;用于python3
使用shlex
模块。
解决方案 4:
您应该将命令连接成整个字符串:
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)
解决方案 5:
您可以通过传递来设置环境
env={whatever_you_want: values} | os.environ
在您的子流程调用中。
解决方案 6:
用例
运行 n 个 shell 脚本或 python 脚本
创建一个 run.py 文件,负责将参数(整数)传递给 shell 文件。
假设您想要运行 n(4) 个接受1 个参数的 shell 脚本或 python 脚本。
使用以下代码创建一个文件run.python 。
下面代码说明
instanceQty = 要运行的 shell 脚本数量
os.getcwd() = 当前文件的路径
mockScript.sh =我将其与run.py放在同一目录中的shell 脚本
运行 shell 脚本
# this is python file with name run.py
import subprocess,os
instanceQty = 4
for i in range(0, instanceQty):
print(os.getcwd())
subprocess.Popen(f"{os.getcwd()}/mockScript.sh {i}",shell=True,executable='/bin/bash')
运行 Python 脚本
import subprocess,os,sys
instanceQty = 4
for i in range(0, instanceQty):
print(os.getcwd())
subprocess.Popen([sys.executable,f"{os.getcwd()}/mockScript.py",str(i)])
使用以下命令运行此文件
python 运行.py
MacOS 上的权限问题
sudo chmod ug+x mockScript.sh
sudo chmod ug+x run.py
所有代码均在 Python 3.8.1 和 MacOs 12.0.1 环境上测试。