Python执行外部命令的六种方式对比与实战

月亮慢半拍
月亮慢半拍
发布时间:2026-10-14 | 2人浏览过
原创

本文详细对比了Python中执行外部命令的多种方法,包括os.system、os.popen、commands模块以及subprocess系列函数。重点解析了阻塞与非阻塞行为的差异,如何捕获标准输出与错误,以及将输出重定向至文件的技巧。同时指出使用PIPE可能导致死锁的风险,并提供check_call与check_output的异常处理建议,帮助开发者选择合适方案。

Python执行外部命令概述

在Python开发中,经常需要调用外部程序或系统命令。常用的实现方式有多种,下面逐一介绍。

首先介绍os.system()方法。该方法执行时会阻塞当前线程,直到外部命令执行完毕。

执行过程中,命令的标准输出会直接打印到控制台屏幕。

函数返回值为命令的执行结果码(exit code),通常0表示成功,非0表示失败。


# -*- encoding=utf-8 -*-
import os
if __name__ == '__main__':
  pass
  print('开始')
  ret1 = os.system('python')
  print('ret1:{}'.format(ret1))
  ret2 = os.system('java -version')
  print('ret2:{}'.format(ret2))
  print('结束')

接下来看具体的运行效果。

有人可能认为阻塞是因为Python解释器本身的机制,为了验证这一点,可以尝试连续执行两个相同的命令,例如都使用java -version进行测试。


# -*- encoding=utf-8 -*-

import os

if __name__ == '__main__':
  pass
  print('开始')
  ret1 = os.system('java -version')
  print('ret1:{}'.format(ret1))
  ret2 = os.system('java -version')
  print('ret2:{}'.format(ret2))
  print('结束')

实际运行结果显示,命令依次执行,前一个命令结束后才开始下一个,体现了阻塞特性。

os.popen()方法详解

第二种方式是os.popen()。

关于阻塞行为,os.popen()的表现取决于后续操作:如果不读取输出,它可能不阻塞或表现不同;如果读取输出,则会阻塞。

与os.system不同,os.popen()执行时,命令的标准输出不会直接打印到屏幕。

该函数返回一个文件对象,开发者可以通过read()或readline()方法读取命令的输出内容。

当不阻塞(即不立即读取输出)时:


# -*- encoding=utf-8 -*-
import os
if __name__ == '__main__':
  pass
  print('开始')
  os.popen('python')
  ret2 = os.popen('java -version')
  print('结束')

运行代码会发现,程序迅速打印“开始”和“结束”,就像只执行了两个print语句一样快,因为os.popen()创建进程后并未立即等待其完成或读取输出。

当阻塞(即调用read()读取输出)时:


# -*- encoding=utf-8 -*-
import os
if __name__ == '__main__':
  pass
  print('开始')
  ret1 = os.popen('python')
  print(ret1.read())
  ret2 = os.popen('java -version')
  print(ret2.read())
  print('结束')

运行代码会看到程序等待命令执行完毕,读取输出后再打印“结束”,体现了阻塞特性。

commands.getstatusoutput()方法

第三种方式是commands.getstatusoutput()。

该方法主要在Python 2.x版本中使用,Python 3中已移除。

需要在Linux环境下运行才能看到完整效果。

其阻塞行为与os.popen()类似,取决于是否读取输出。

执行时,命令的标准输出不会直接打印到屏幕。

在非阻塞情况下:


# -*- encoding=utf-8 -*-
import commands
if __name__ == '__main__':
  pass
  print('开始')
  ret1 = commands.getstatusoutput('python')
  ret2 = commands.getstatusoutput('java -version')
  print('结束')

运行代码,程序快速执行并返回结果元组(状态码和输出),不等待命令完全结束或打印输出。

在阻塞情况下:


# -*- encoding=utf-8 -*-
import commands
if __name__ == '__main__':
  pass
  print('开始')
  ret1 = commands.getstatusoutput('python')
  print ret1[0]
  print ret1[1]
  print '=============='
  ret2 = commands.getstatusoutput('java -version')
  print ret2[0]
  print ret2[1]
  print '=============='
  print('结束')

运行代码,程序会等待命令执行完毕,返回包含状态码和完整输出的元组。

subprocess.Popen高级用法

第四种方式是subprocess模块中的Popen类。

Popen的阻塞行为同样取决于是否读取IO:如果不读取,可能不阻塞;如果读取或调用wait(),则会阻塞。

默认情况下,命令的标准输出会打印到屏幕。

可以通过stdout和stderr参数指定将标准输出或错误输出重定向到文件。


# -*- encoding=utf-8 -*-
import subprocess
if __name__ == '__main__':
  pass
  print('开始')
  s1 = subprocess.Popen('python')
  s1.wait()
  s2 = subprocess.Popen('java -version')
  s2.wait()
  print('结束')

下面展示Popen的基本运行示例。

4.1、将输出重定向到文件。

通过传入文件对象给stdout参数,可以将命令输出写入指定文件。


# -*- encoding=utf-8 -*-
import subprocess
if __name__ == '__main__':
  pass
  print('开始')
  with open('s1.txt', 'w') as f:
    s1 = subprocess.Popen('python', stdout=f, ) # 指定输出重定向到文件
    s1.wait(10) # 超过10秒还没执行完会抛出异常
  with open('s2.txt', 'w') as f:
    s2 = subprocess.Popen('java -version', stdout=f, stderr=f) # 指定输出和错误都重定向到文件
    s2.wait()
  print('结束')

运行代码后,可以看到指定文件已被写入内容,而屏幕没有打印输出。需要注意区分stdout和stderr参数,分别处理标准输出和错误输出。

4.2、捕获输出和错误信息。

使用subprocess.PIPE可以创建一个新的管道来接收命令的运行状态和输出。


# -*- encoding=utf-8 -*-

import subprocess

if __name__ == '__main__':
  pass
  print('开始')
  s1 = subprocess.Popen('java -version', stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  s1.wait(10) # 超过10秒还没执行完会抛出异常
  print(s1.returncode) # 结果码
  print(s1.poll()) # 已经执行结束返回结果码,否则返回None
  std1 = s1.stdout.read()
  print('std_out:{}'.format(std1))
  std2 = s1.stderr.read()
  print('std_err:{}'.format(std2))
  print('结束')

运行代码,通过读取管道内容获取命令的输出和错误信息。

subprocess.call与check系列函数

第五种方式是subprocess.call()。

该方法旨在替代os.system(),提供更灵活的控制。

subprocess.call()默认是阻塞的,直到命令执行完毕。


# -*- encoding=utf-8 -*-
import subprocess
if __name__ == '__main__':
  pass
  print('开始')
  s1 = subprocess.call('java -version', )
  print(s1)
  s2 = subprocess.call('python', )
  print(s2)
  s3 = subprocess.call('python', timeout=3) # 超时抛出异常
  print(s3)
  print('结束')

第六种是check_call()和check_output()。

这两个函数都会检查命令的结果码,如果结果码不是0,就会抛出异常。

check_call()函数返回命令的状态码。

check_output()函数返回命令的标准输出内容。


# -*- encoding=utf-8 -*-
import subprocess
if __name__ == '__main__':
  pass
  print('开始')
  s1 = subprocess.check_call('java -version', )
  print(s1)
  s2 = subprocess.check_output('java -version', )
  print(s2)
  print('结束')

运行代码示例,可以看到成功执行时返回状态码或输出内容。

使用call()、check_call()和check_output()时,尽量避免将参数stderr和stdout设置为subprocess.PIPE。因为这些函数默认会等待子进程完成,如果子进程产生大量输出导致管道堵塞,而父进程仍在等待子进程完成,可能会造成死锁。

以上就是Python中执行外部命令的几种常见方式及其注意事项,希望本文能帮助大家更好地掌握相关技术。

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表