【问题标题】:Invoking C compiler using Python subprocess command使用 Python 子进程命令调用 C 编译器
【发布时间】:2014-09-04 11:05:54
【问题描述】:

我正在尝试使用 Python 编译 C 程序,并希望使用“

./a.out <inp.txt works

但类似地,如果我尝试使用 Python 脚本执行此操作,它并没有达到预期的效果。 例如:

import subprocess
subprocess.call(["gcc","a.c","-o","x"])
subprocess.call(["./x"])

import subprocess
subprocess.call(["gcc","a.c","-o","x"])
subprocess.call(["./x","<inp.txt"])

两个脚本都通过终端请求输入。但我认为在第二个脚本中它应该从文件中读取。为什么这两个程序的工作方式相同?

【问题讨论】:

    标签: python c shell


    【解决方案1】:

    补充@Jonathan Leffler's@alastair's 有用的答案:

    假设 控制您传递给 shell 以执行的字符串,我认为为了方便起见使用 shell 没有任何问题。 [1]

    subprocess.call() 有一个可选的布尔值shell 参数,它导致命令被传递到shell,启用I/O 重定向,引用环境变量, ...:

    subprocess.call("./x <inp.txt", shell = True)
    

    注意整个命令行是如何作为单个字符串而不是参数数组传递的。


    [1]避免在以下情况下使用 shell:

    • 如果您的 Python 代码必须在其他平台上运行,而不是类 Unix 平台,例如 Windows
    • 如果性能至关重要。
    • 如果您发现自己在 Python 端更好地处理“外包”任务。

    如果您担心 shell 环境缺乏可预测性(就像 @alastair 一样):

    • subprocess.callshell = True always 创建 /bin/sh 的非交互式非登录实例 - 请注意,它不是 用户的 默认值使用的外壳。
    • sh 不读取初始化文件 用于非交互式非登录 shell(既不是系统范围的也不是用户特定的)。
    • 请注意,即使在sh 伪装成bash 的平台上,bash 在作为sh 调用时也会以这种方式运行。
    • 每个使用subprocess.callshell = True 创建的shell 实例都是它自己的世界,它的环境既不受以前的shell 实例影响,也不会影响以后的shell 实例。
    • 但是,创建的shell实例继承了python进程本身的环境
    • 如果您从交互式 shell 启动 Python 程序,则会继承该 shell 的环境。请注意,这仅当前工作目录环境变量有关,与别名、shell 函数和shell 变量无关。
    • 一般来说,这是一个功能,因为 Python (CPython) 本身被设计为可通过环境变量进行控制(对于 2.x,请参阅 https://docs.python.org/2/using/cmdline.html#environment-variables; 对于 3.x,见https://docs.python.org/3/using/cmdline.html#environment-variables)。
    • 如果需要,您可以通过env 参数提供您自己的环境到shell;但是请注意,您必须在该事件中提供整个环境,如果需要,可能包括USERHOME 等变量;简单的例子,明确定义$PATH
    • subprocess.call('echo $PATH', shell = True, \ env = { 'PATH': '/sbin:/bin:/usr/bin' })

    【讨论】:

    • “为了方便而使用shell没有错”吗?你会这么认为,但是如果例如,会发生什么?用户设置 IFS?或者,由于上面对 gcc 的调用没有指定完整路径,PATH?如果可以避免的话,最好不要使用 shell 来运行它。
    • @alastair: $IFS$PATH 只能(暂时)从您正在执行的同一 shell 命令中更改;查看我关于如何创建 shell 实例的更新,如果您仍然对修改后的环境有疑虑,请告诉我(我很可能遗漏了一些东西)。也就是说,避免使用 shell 的一个令人信服的理由是,如果您还需要支持 Windows(除非您故意只使用简单到足以在两种平台类型上工作的命令)。
    • 根据subprocess 文档,除非明确指定环境,否则它是从 Python 进程继承的。如果有人可以操纵 那个 进程的环境,它仍然可能导致不良行为。
    • @alastair:这值得一提,但这更像是一个功能而不是一个陷阱 - 请参阅我的更新。
    • 当然。我仍然认为通常不鼓励从代码中使用 shell,尤其是在有一个简单的非基于 shell 的替代方案可用的情况下。也就是说,我对你的答案投了赞成票,因为它现在已经很全面了 IMO :-)
    【解决方案2】:

    shell 为进程执行 I/O 重定向。根据您所说的,subprocess 模块不会像那样进行 I/O 重定向。为了演示,运行:

    subprocess.call(["sh","-c", "./x <inp.txt"])
    

    运行 shell 并且应该重定向 I/O。使用您的代码,您的程序 ./x 被赋予了一个参数 &lt;inp.txt,它被忽略了。

    注意:对subprocess.call 的替代调用纯粹是出于诊断目的,不是推荐的解决方案。推荐的解决方案包括阅读 (Python 2) subprocess 模块文档(或它的 Python 3 文档)以了解如何使用模块进行重定向。

    import subprocess
    i_file = open("inp.txt")
    subprocess.call("./x", stdin=i_file)
    i_file.close()
    

    如果您的脚本即将退出,因此您不必担心浪费的文件描述符,您可以将其压缩为:

    import subprocess
    subprocess.call("./x", stdin=open("inp.txt"))
    

    【讨论】:

      【解决方案3】:

      默认情况下,subprocess 模块不会将参数传递给 shell。为什么?因为通过 shell 运行命令是危险的;除非它们被正确引用和转义(这很复杂),否则通常可以说服执行此类操作的程序运行不需要的和意外的 shell 命令。

      无论如何,使用 shell 是错误的。如果要从特定文件中获取输入,可以使用subprocess.Popen,将stdin 参数设置为文件inp.txt 的文件描述符(您可以通过调用fileno() 一个Python 文件对象来获取文件描述符)。

      【讨论】:

        猜你喜欢
        • 2021-04-11
        • 2015-01-13
        • 1970-01-01
        • 2015-06-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-27
        • 1970-01-01
        相关资源
        最近更新 更多