【问题标题】:Passing Variables to Subprocess.Popen将变量传递给 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 不适合您的子进程等)。

标签: python subprocess


【解决方案1】:

删除shell=TrueThe 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() 以减少注入的可能性。
    注意 2pipes.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 环境下测试。

    【讨论】:

      猜你喜欢
      • 2020-06-21
      • 2014-02-13
      • 1970-01-01
      • 2016-09-08
      • 2017-02-18
      • 1970-01-01
      • 2020-01-24
      • 2018-06-17
      • 1970-01-01
      相关资源
      最近更新 更多