将变量传递给 Subprocess.Popen
Posted
技术标签:
【中文标题】将变量传递给 Subprocess.Popen【英文标题】:Passing Variables to Subprocess.Popen 【发布时间】:2013-12-07 01:18:27 【问题描述】:我有一个脚本,它通过 subprocess.Popen 模块调用另一个 python 脚本。但是由于我将参数存储在变量中
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)
【问题讨论】:
使用变量构建作为命令的字符串,或将它们作为参数列表传递。 将 Python 称为 Python 的子进程本身就是一种反模式;更好的解决方案通常是从子进程重构脚本,以便您可以import
它并直接从父脚本调用它。 在种情况下,您确实需要子进程(例如,如果脚本使用了您需要在父进程中以不同方式处理的信号),但通常情况下,您可能不应该这样做。
另外,正如subprocess
文档中已经指出的那样,如果您的用例已经由subprocess.run()
和朋友中的一个高级函数处理,您应该避免使用Popen
.基本上,除非您需要并行处理或与正在运行的进程交互,否则不要使用Popen
(如果这样做,请注意等待Popen
不适合您的子进程等)。
【参考方案1】:
删除shell=True
。 The arguments to Popen()
are treated differently on Unix if 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
传递给带有外部输入的命令存在安全隐患,如警告in the docs 所述。
【讨论】:
【参考方案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
)。但是如果可能的话,你最好使用列表版本 - 它可以更简单地控制命令行的拆分方式(例如,如果参数中有空格或引号),而不必乱用引号字符。
【讨论】:
我用来存储输出的变量,但在打印时它是空的(我猜它还没有被执行)。 我想你的意思是当你从 p.stdout 读取时,没有输出?那是因为该命令没有运行。 实际上,shell=True 可能在这里弄得一团糟——除非您使用 globbing(例如,展开文件列表),否则最好将其关闭。 没有shell=true,脚本拒绝执行,显示“sh: mytool.sh command not found” 输入 mytool.sh 的完整路径——在命令行中输入“mytool.sh”时给出的任何内容。【参考方案3】:第一个 Popen
参数类型为 str
的问题。将其替换为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:用 shell=True
构造的 command
可能不安全。使用 pipes.quote()
以减少注入的可能性。注意 2:pipes.quote()
已弃用,因为 python2
;对于python3
使用shlex
模块。
【讨论】:
遇到了和以前类似的问题,但我现在解决了。谢谢:) 如果您传递带有shell=True
的列表,第一个参数将被视为一个shell 脚本,随后的参数作为该脚本的参数(它的$0
、$1
等)。这个shlex.split()
操作的结果不是一个打算以这种方式解析的列表。在实践中,它将运行python
,不带任何参数。
相比上半场有了很大的进步。回复:后半部分,更好地展示使用pipes.quote()
(或在Python 3 中shlex.quote()
)来创建可以安全地替换为shell命令的内容,而不是仅仅放入不安全的代码有一堆警告。
@CharlesDuffy,已更新现在我无法查看上次更新。请检查一下,谢谢。【参考方案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)
【讨论】:
我已将输出保存到变量中,但没有输出或正在执行的脚本的跟踪。可能是直接输出错误来检查一下吗? 也添加“stderr=subprocess.PIPE” 如果有人给你一个包含$(rm -rf ~)
的地址列表,你最好希望没有人使用这种基于连接的方法。这是非常不安全的;即使您确实想使用shell=True
,(唯一)安全的方法是从脚本代码带外传递变量:p = subprocess.Popen(['python mytool.py -a "$1" -x "$2" -p "$3" some additional command', '_', str(servers[server]['address']), str(servers[server]['port']), str(servers[server]['pass'])], shell=True, stdout=subprocess.PIPE)
【参考方案5】:
用例
运行n个shell脚本或python脚本
创建一个 run.py 文件,负责将参数(整数)传递给 shell 文件。
假设您喜欢运行 n(4) 个接受 1 参数的 shell 脚本或 python 脚本。
使用以下代码创建文件run.python。
以下代码说明
instanceQty = 要运行的 shell 脚本数量 os.getcwd() = 当前文件的路径 mockScript.sh = shell 脚本 我与 run.py 放在同一目录中运行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 环境下测试。
【讨论】:
以上是关于将变量传递给 Subprocess.Popen的主要内容,如果未能解决你的问题,请参考以下文章