使用Python子进程命令调用C编译器
我正在尝试用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 个回答
默认情况下,subprocess
模块不会将参数传递给命令行。这是为什么呢?因为通过命令行运行指令是很危险的;如果没有正确地处理引号和转义字符(这很复杂),就很容易让程序执行一些不想要的、意外的命令。
而且,使用命令行来做这些事情本身就是不对的。如果你想从特定的文件中获取输入,可以使用subprocess.Popen
,并把stdin
参数设置为文件inp.txt
的文件描述符(你可以通过调用fileno()
来获取一个Python文件对象的文件描述符)。
在计算机中,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"))
为了补充@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.call
和shell = 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;不过,注意在这种情况下,你必须提供整个环境,可能还需要包括USER
和HOME
等变量;简单的例子,明确地定义$PATH
:subprocess.call('echo $PATH', shell = True, \ env = { 'PATH': '/sbin:/bin:/usr/bin' })