Python用subprocess的Popen来调用系统命令

本文详细介绍了Python中的subprocess模块,特别是Popen类的使用方法,包括构造函数参数、常用方法等,并提供了实例代码演示如何与控制台应用程序进行交互。

当我们需要调用系统的命令的时候,最先考虑的os模块。用os.system()和os.popen()来进行操作。但是这两个命令过于简单,不能完成一些复杂的操作,如给运行的命令提供输入或者读取命令的输出,判断该命令的运行状态,管理多个命令的并行等等。这时subprocess中的Popen命令就能有效的完成我们需要的操作。在这里对Popen予以简单介绍。

下面是一个很简单的例子,来自Python的官网教程:http://docs.python.org/library/subprocess.html

 

>>> import shlex, subprocess
>>> command_line = raw_input()
/bin/vikings -input eggs.txt -output "spam spam.txt" -cmd "echo '$MONEY'"
>>> args = shlex.split(command_line)
>>> print args
['/bin/vikings', '-input', 'eggs.txt', '-output', 'spam spam.txt', '-cmd', "echo '$MONEY'"]
>>> p = subprocess.Popen(args) # Success!

 

Popen它的构造函数如下:

 

subprocess.Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)

 

参数args可以是字符串或者序列类型(如:list,元组),用于指定进程的可执行文件及其参数。如果是序列类型,第一个元素通常是可执行文件的路 径。我们也可以显式的使用executeable参数来指定可执行文件的路径。在windows操作系统上,Popen通过调用 CreateProcess()来创建子进程,CreateProcess接收一个字符串参数,如果args是序列类型,系统将会通过 list2cmdline()函数将序列类型转换为字符串。

参数bufsize:指定缓冲。我到现在还不清楚这个参数的具体含义,望各个大牛指点。


参数executable用于指定可执行程序。一般情况下我们通过args参数来设置所要运行的程序。如果将参数shell设为 True,executable将指定程序使用的shell。在windows平台下,默认的shell由COMSPEC环境变量来指定。


参数stdin, stdout, stderr分别表示程序的标准输入、输出、错误句柄。他们可以是PIPE,文件描述符或文件对象,也可以设置为None,表示从父进程继承。


参数preexec_fn只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用。


参数Close_sfs:在windows平台下,如果close_fds被设置为True,则新创建的子进程将不会继承父进程的输入、输出、错误管 道。我们不能将close_fds设置为True同时重定向子进程的标准输入、输出与错误(stdin, stdout, stderr)。


如果参数shell设为true,程序将通过shell来执行。


参数cwd用于设置子进程的当前目录。


参数env是字典类型,用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。


参数Universal_newlines:不同操作系统下,文本的换行符是不一样的。如:windows下用’/r/n’表示换,而Linux下用 ‘/n’。如果将此参数设置为True,Python统一把这些换行符当作’/n’来处理。


参数startupinfo与createionflags只在windows下用效,它们将被传递给底层的CreateProcess()函数,用 于设置子进程的一些属性,如:主窗口的外观,进程的优先级等等。

subprocess.PIPE
在创建Popen对象时,subprocess.PIPE可以初始化stdin, stdout或stderr参数。表示与子进程通信的标准流。

subprocess.STDOUT
创建Popen对象时,用于初始化stderr参数,表示将错误通过标准输出流输出。

Popen的方法:

Popen.poll() 
用于检查子进程是否已经结束。设置并返回returncode属性。

Popen.wait() 
等待子进程结束。设置并返回returncode属性。

Popen.communicate(input=None)
与子进程进行交互。向stdin发送数据,或从stdout和stderr中读取数据。可选参数input指定发送到子进程的参数。 Communicate()返回一个元组:(stdoutdata, stderrdata)。注意:如果希望通过进程的stdin向其发送数据,在创建Popen对象的时候,参数stdin必须被设置为PIPE。同样,如 果希望从stdout和stderr获取数据,必须将stdout和stderr设置为PIPE。

Popen.send_signal(signal) 
向子进程发送信号。

Popen.terminate()
停止(stop)子进程。在windows平台下,该方法将调用Windows API TerminateProcess()来结束子进程。

Popen.kill()
杀死子进程。

Popen.stdin 
如果在创建Popen对象是,参数stdin被设置为PIPE,Popen.stdin将返回一个文件对象用于策子进程发送指令。否则返回None。

Popen.stdout 
如果在创建Popen对象是,参数stdout被设置为PIPE,Popen.stdout将返回一个文件对象用于策子进程发送指令。否则返回 None。

Popen.stderr 
如果在创建Popen对象是,参数stdout被设置为PIPE,Popen.stdout将返回一个文件对象用于策子进程发送指令。否则返回 None。

Popen.pid 
获取子进程的进程ID。

Popen.returncode 
获取进程的返回值。如果进程还没有结束,返回None。

下面是一个非常简单的例子,来演示supprocess模块如何与一个控件台应用程序进行交 互。

import subprocess   

p = subprocess.Popen(“app2.exe”, stdin = subprocess.PIPE, /   
stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = False)   

p.stdin.write(’3/n’)   
p.stdin.write(’4/n’)   
print p.stdout.read()   

#—- 结果 —-   
input x:    
input y:    
3 + 4 = 7 

app2.exe也是一个非常简单的控制台程序,它从界面上接收两个数值,执行加操作,并将结 果打印到控制台上。代码如下:

#include <iostream>   
using namespace std;   

int main(int argc, const char *artv[])   
{   
int x, y;   
cout << “input x: ” << endl;   
cin >> x;   
cout << “input y: ” << endl;   
cin >> y;   
cout << x << ” + ” << y << ” = ” << x + y << endl;   

return 0;   

 

supprocess模块提供了一些函数,方便我们用于创建进程。

subprocess.call(*popenargs, **kwargs)
运行命令。该函数将一直等待到子进程运行结束,并返回进程的returncode。文章一开始的例子就演示了call函数。如果子进程不需要进行交 互,就可以使用该函数来创建。

subprocess.check_call(*popenargs, **kwargs) 
与subprocess.call(*popenargs, **kwargs)功能一样,只是如果子进程返回的returncode不为0的话,将触发CalledProcessError异常。在异常对象中,包 括进程的returncode信息。

subprocess模块的内容就这么多。在Python手册中,还介绍了如何使用 subprocess来替换一些老的模块,老的函数的例子。赶兴趣的朋友可以看一下。

参考文档:

subprocess — Subprocess management

PyMoTW:subprocess

http://hi.baidu.com/kobeantoni/blog/item/a034bce9d0e01bdfd539c9a4.html

 

<think>我们正在讨论Python 3中使用subprocess.Popen执行命令的方法和示例。根据引用[1]和引用[2]的信息,我们可以知道subprocess.Popen用于执行外部命令,并且可以与其交互。同时,引用[2]提供了一个示例,展示了如何执行命令并读取输出(包括处理编码问题)。 引用[3]则提到了subprocess.run()和subprocess.Popen()的区别,但用户的问题集中在Popen的使用上,因此我们将重点放在Popen。 下面我将分步骤说明如何使用subprocess.Popen执行命令: 1. 导入subprocess模块 2. 使用Popen构造函数创建子进程,传入参数包括命令(字符串或列表)、shell参数、标准输出和标准错误的重定向设置等。 3. 与子进程交互,例如读取输出、发送输入、等待完成等。 注意:在Python 3中,输出通常为字节串(bytes),因此需要解码(decode)为字符串。同时,为了避免乱码,需要确保使用正确的编码(如'utf-8')。 示例1:执行一个简单的命令(例如,执行一个Python脚本)并捕获其输出。 ```python import subprocess # 使用Popen执行命令,将stdout重定向到管道,以便我们可以读取 # 注意:这里使用列表形式传递命令,以避免shell注入攻击,同时设置stdout和stderr # 如果命令简单且安全,也可以使用字符串形式并设置shell=True,但要注意安全性 process = subprocess.Popen( ['python', 'test.py'], # 命令和参数列表 stdout=subprocess.PIPE, # 将标准输出重定向到管道 stderr=subprocess.PIPE # 将标准错误重定向到管道 ) # 等待进程完成并获取输出 stdout, stderr = process.communicate() # 将字节输出解码为字符串(假设输出是utf-8编码) if stdout: print("标准输出:", stdout.decode('utf-8')) if stderr: print("标准错误:", stderr.decode('utf-8')) ``` 示例2:实时读取输出(逐行读取),如引用[2]所示: ```python import subprocess # 构造Popen对象,注意这里使用字符串命令并设置shell=True,也可以使用列表而不需要shell=True # 但注意,使用shell=True可能有安全风险,如果命令字符串来自用户输入,则要谨慎 p = subprocess.Popen( 'python test2.py', # 命令字符串,因为设置了shell=True shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) # 逐行读取输出(标准输出) # 注意:iter(p.stdout.readline, b'') 会一直读取直到遇到空字节串 for line in iter(p.stdout.readline, b''): # 解码并输出,去掉行尾的换行符(print本身会换行,所以可以用end='') print(line.decode('utf-8'), end='') # 关闭管道 p.stdout.close() # 等待进程结束 p.wait() # 如果有错误,也可以类似地读取标准错误(但上面的循环只读取了标准输出,标准错误可以单独处理) # 注意:同时读取stdout和stderr可能会导致死锁,如果错误流很大,最好使用上面的communicate()或者分别读取 ``` 重要提示: - 使用`communicate()`可以一次性获取所有输出,适用于输出量不大的情况。对于大量输出,建议使用逐行读取。 - 设置`shell=True`可以让命令通过系统shell执行(如/bin/sh或cmd.exe),这样可以使用shell的特性(如通配符、管道等),但要注意安全风险(如果命令字符串来自不可信的输入,可能导致命令注入)。 - 推荐使用列表形式传递命令,这样不需要shell=True,并且可以避免shell注入攻击。 另外,关于编码问题:如果遇到乱码,可能是因为命令输出的编码与Python解码使用的编码不一致。在Windows上,命令输出通常是GBK编码,而Linux/macOS通常是UTF-8。因此,可能需要根据系统调整解码方式,例如: ```python # 在Windows上可能需要使用'gbk' print(line.decode('gbk'), end='') ``` 或者使用当前系统的默认编码: ```python import sys encoding = sys.stdout.encoding # 获取当前控制台的编码,但注意这不一定和命令输出的编码一致 ``` 所以,最可靠的方法是知道被调用程序的输出编码。 相关问题: 1. 如何避免使用subprocess.Popen时出现死锁? 2. subprocess.Popensubprocess.run有什么区别? 3.Windows和Linux下使用subprocess.Popen处理编码问题有什么不同? 4. 如何通过subprocess.Popen向子进程输入数据? </think>### Python 3中`subprocess.Popen`使用指南 `subprocess.Popen`是Python执行外部命令的核心工具,允许细粒度控制子进程。以下是关键使用方法和示例: #### 一、基础用法 ```python import subprocess # 执行简单命令 process = subprocess.Popen( ['ls', '-l'], # 命令参数列表 stdout=subprocess.PIPE, # 捕获标准输出 stderr=subprocess.PIPE, text=True # Python 3.7+ 自动解码为字符串 ) stdout, stderr = process.communicate() # 等待进程结束 print(stdout) ``` #### 二、实时输出处理 ```python process = subprocess.Popen( ['ping', 'google.com'], stdout=subprocess.PIPE, bufsize=1, # 行缓冲 universal_newlines=True # 文本模式 ) # 逐行读取输出 while True: line = process.stdout.readline() if not line: break print(line.strip()) process.wait() # 等待进程退出 ``` #### 三、解决乱码问题 ```python # 显式指定编码(Windows通常为'gbk',Linux为'utf-8') process = subprocess.Popen( 'dir', # Windows命令 shell=True, stdout=subprocess.PIPE, encoding='gbk' # 关键解决中文乱码 ) output = process.communicate()[0] print(output) ``` #### 四、输入交互示例 ```python process = subprocess.Popen( ['python'], # 启动Python交互环境 stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True ) # 发送命令并获取输出 commands = "print('Hello!')\nexit()\n" stdout, _ = process.communicate(commands) print(stdout) # 输出: Hello! ``` #### 五、高级控制 ```python # 超时控制 try: process = subprocess.Popen(['sleep', '10']) process.wait(timeout=5) # 5秒后超时 except subprocess.TimeoutExpired: process.terminate() # 环境变量设置 env = {'PATH': '/usr/local/bin'} subprocess.Popen(['echo', '$PATH'], env=env, shell=True) ``` #### 六、安全注意事项 1. **避免`shell=True`**:除非需要shell特性(如管道`|`),否则应使用列表传参防止注入攻击 ```python # 不安全 subprocess.Popen(f"rm {user_input}", shell=True) # 安全替代 subprocess.Popen(['rm', user_input]) ``` 2. **资源清理**:始终使用`with`块或显式调用`wait()/terminate()` --- ### 关键区别(与`subprocess.run`对比) | 特性 | `Popen` | `run()` | |---------------------|------------------------------------|-----------------------------| | 实时交互 | ✅ 支持 | ❌ 一次性返回所有输出 | | 超时控制 | 需手动实现 | ✅ 内置`timeout`参数 | | 后台执行 | ✅ `process.detach()` | ❌ 总是阻塞 | | 输入/输出流控制 | ✅ 精细控制 | ️ 有限控制 | | 错误处理 | 需手动检查返回码 | ✅ 自动抛出`CalledProcessError` | > 推荐:简单任务用`run()`,复杂交互用`Popen`[^3] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值