使用Python子进程命令调用C编译器

3 投票
3 回答
5430 浏览
提问于 2025-04-18 13:14

我正在尝试用Python编译一个C程序,并想通过"<"这个操作符来输入数据,但效果并不如我所想的那样。

./a.out <inp.txt works

如果我直接编译这个C程序并通过文件输入数据,它是可以正常工作的;比如说:

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

但是如果我用Python脚本来做同样的事情,结果却没有达到预期。

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

这两个脚本都要求在终端输入数据。但我觉得第二个脚本应该是从文件中读取数据,为什么这两个程序的表现是一样的呢?

3 个回答

2

默认情况下,subprocess模块不会将参数传递给命令行。这是为什么呢?因为通过命令行运行指令是很危险的;如果没有正确地处理引号和转义字符(这很复杂),就很容易让程序执行一些不想要的、意外的命令。

而且,使用命令行来做这些事情本身就是不对的。如果你想从特定的文件中获取输入,可以使用subprocess.Popen,并把stdin参数设置为文件inp.txt的文件描述符(你可以通过调用fileno()来获取一个Python文件对象的文件描述符)。

4

在计算机中,shell(命令行)可以为一个进程处理输入和输出的重定向。根据你说的情况,subprocess模块并不像那样进行输入输出的重定向。为了演示这一点,可以运行:

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

这条命令会启动shell,并且应该会重定向输入输出。根据你的代码,你的程序./x接收了一个参数<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"))
6

为了补充@Jonathan Leffler的@alastair的有用回答:

假设控制着要传给shell执行的字符串,我觉得使用shell来方便操作是没问题的。[1]

subprocess.call()有一个可选的布尔参数shell,这个参数可以让命令通过shell来执行,这样就可以进行输入输出重定向、引用环境变量等等:

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

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


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

  • 如果你的Python代码必须在Unix以外的平台上运行,比如Windows
  • 如果性能非常重要。
  • 如果你发现自己在“外包”一些更适合在Python中处理的任务。

如果你担心shell环境的不确定性(就像@alastair所说的):

  • subprocess.call使用shell = True总是创建非交互式的非登录实例的/bin/sh - 注意,这不是使用用户的默认shell。

  • sh 不会读取非交互式非登录shell的初始化文件(无论是系统范围的还是用户特定的)。

    • 注意,即使在sh实际上是bash的情况下,当以sh的方式调用时,bash也会这样表现。
  • 每个使用subprocess.callshell = True创建的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' })
      

撰写回答