【问题标题】:In Python, how I do use subprocess instead of os.system?在 Python 中,我如何使用子进程而不是 os.system?
【发布时间】:2009-01-07 17:24:50
【问题描述】:

我有一个 Python 脚本,它调用带有各种参数的可执行程序(在本例中,它是“sqlpubwiz.exe”,即“Microsoft SQL Server 数据库发布向导”):

import os

sqlpubwiz = r'"C:\Program Files\Microsoft SQL Server\90\Tools\Publishing\sqlpubwiz.exe"'
server = 'myLocalServer'
database = 'myLocalDatabase'
connection_values = ['server=' + server, 'database=' + database, 'trusted_connection=true']
connection_string = ';'.join(connection_values)
dbms_version = '2000'
sqlscript_filename = 'CreateSchema.sql'

args = [
        sqlpubwiz,
        'script',
        '-C ' + connection_string,
        sqlscript_filename,
        '-schemaonly',
        '-targetserver ' + dbms_version,
        '-f',
]

cmd = ' '.join(args)
os.system(cmd)

此代码运行正常,但我想养成使用subprocess 的习惯,因为它旨在替换 os.system。但是,经过几次失败的尝试后,我似乎无法使其正常工作。

如果将上面的代码转换为使用 subprocess 代替 os.system 会是什么样子?

【问题讨论】:

    标签: python syntax scripting process


    【解决方案1】:
    import subprocess
    p=subprocess.Popen(args, stdout=subprocess.PIPE)
    print p.communicate()[0]
    

    看起来几乎一样。但是路径不应该是 r'"无论路径是什么"'。因为这给了我一个错误。您想要“带有转义反斜杠的路径”或 r'没有转义的路径'。

    args 的形式也应该是 ['-arg', 'args'] 而不是 ['arg argsval']。

    【讨论】:

    • 我试过了,但出现以下错误:WindowsError: [错误 3] 系统找不到指定的路径
    • 我认为 Popen 将 args 作为一个序列,而不是一个以空格分隔的大字符串。尝试只传递 'args' 而不是 'cmd'。
    • 删除引号允许脚本执行没有错误,但没有创建 sql 文件。如何查看 exe 的控制台输出?
    • 现在我看到了来自 sqlpubwiz exe 的错误。它显示: >缺少/错误数量的参数。 >无法识别的命令行参数“-targetserver 2000”顺便说一句,我使用的是“args”列表而不是“cmd”。不知道为什么 sqlpubwiz 不喜欢使用 subprocess 与 os.system 将参数传递给它的方式?
    • (这就是为什么我在子进程与仅使用 os.system 方面没有太多运气的原因)
    【解决方案2】:

    从可执行文件的名称中删除引号。在您示例的第一行,而不是

    sqlpubwiz = r'"C:\Program Files\Microsoft SQL Server\90\Tools\Publishing\sqlpubwiz.exe"'
    

    使用:

    sqlpubwiz = r'C:\Program Files\Microsoft SQL Server\90\Tools\Publishing\sqlpubwiz.exe'
    

    这是因为你不必逃避任何东西,因为不会涉及到 shell。

    然后只需使用subprocess.call(args)(不要join args,将它们作为列表传递)

    如果你想捕获输出(os.system 做不到),只需遵循subprocess 文档:

    result = subprocess.Popen(args, stdout=subprocess.PIPE).communicate()[0]
    print result
    

    【讨论】:

    • 使用 subprocess.call(args) 给了我与 Carlos Rendon 相同的错误:>缺少/错误数量的参数。 >无法识别的命令行参数“-targetserver 2000”。
    • 子进程的 args 值是否需要有所不同,而不是在 os.system 的字符串命令中建立起来?
    【解决方案3】:

    以下是我根据Carlos Rendon(和nosklo)的帮助和建议修改后的代码:

    # import os
    import subprocess    
    
    sqlpubwiz = r'C:\Program Files\Microsoft SQL Server\90\Tools\Publishing\sqlpubwiz.exe'
    server = 'myLocalServer'
    database = 'myLocalDatabase'
    connection_values = ['server=' + server, 'database=' + database, 'trusted_connection=true']
    connection_string = ';'.join(connection_values)
    dbms_version = '2000'
    sqlscript_filename = 'CreateSchema.sql'       
    
    args = [
                sqlpubwiz,
                'script',
                '-C',
                connection_string,
                sqlscript_filename,
                '-schemaonly',
                '-targetserver',
                dbms_version,
                '-f',
        ]   
    
    # cmd = ' '.join(args)
    # os.system(cmd)
    
    subprocess.call(args)
    

    (注意:包含空格的原始参数值需要转换为单独的列表项。)

    【讨论】:

      【解决方案4】:

      仅供参考,subprocess 有一个 list2cmdline() 函数,可以让您看到 Popen 将使用的字符串。

      你的版本给出:

      '"C:\\Program Files\\Microsoft SQL Server\\90\\Tools\\Publishing\\sqlpubwiz.exe" script "-C server=myLocalServer;database=myLocalDatabase;trusted_connection=true" CreateSchema.sql -schemaonly "-targetserver 2000" -f'
      

      "-C server=myLocalServer;database=myLocalDatabase;trusted_connection=true""-targetserver 2000" 周围加上额外的引号。

      格式正确:

      args = [
              sqlpubwiz,
              'script',
              '-C', connection_string,
              sqlscript_filename,
              '-schemaonly',
              '-targetserver', dbms_version,
              '-f',
      ]
      

      给予:

      '"C:\\Program Files\\Microsoft SQL Server\\90\\Tools\\Publishing\\sqlpubwiz.exe" script -C server=myLocalServer;database=myLocalDatabase;trusted_connection=true CreateSchema.sql -schemaonly -targetserver 2000 -f'
      

      另外,小问题,但将诸如args 之类的序列制作为不需要可变为元组而不是列表的序列是一个好习惯。

      【讨论】:

        【解决方案5】:

        请记住 os.system 使用 shell,所以你必须真正通过

        shell=True
        

        到 Popen 构造函数/调用以正确模拟它。当然,您实际上可能不需要 shell,但它确实存在。

        【讨论】:

        • 只是回答问题:)
        【解决方案6】:

        这不是您问题的直接答案,但我认为它可能会有所帮助。

        如果您想要更精细地控制返回的异常处理等内容,您还可以查看pexpect。我曾在我调用的进程不一定以正常状态信号退出的情况下使用它,或者我想与它进行更多交互。这是一个非常方便的功能。

        【讨论】:

          【解决方案7】:

          Windows 命令将接受正斜杠“/”代替路径名中的反斜杠,因此您可以使用前者来避免在命令字符串中转义反斜杠。不完全是您问题的答案,但可能对了解很有用。

          【讨论】:

            猜你喜欢
            • 2011-09-12
            • 2012-03-15
            • 2021-06-10
            • 2016-05-23
            • 1970-01-01
            • 1970-01-01
            • 2019-08-18
            • 2020-08-19
            • 2015-08-10
            相关资源
            最近更新 更多