首页 > 其他分享 >subprocess.run 和 subprocess.Popen 区别

subprocess.run 和 subprocess.Popen 区别

时间:2024-05-20 09:12:20浏览次数:14  
标签:run stdout stdin Popen subprocess PIPE stderr

subprocess.run:是阻塞式,非交互性,返回值:不是我们想要的执行结果或相关信息,而是一个 CompletedProcess 类型对象

subprocess.Popen:非阻塞式,交互性,返回值:是一个Popen对象,

<subprocess.Popen object at 0x0000000002B17668>
Popen对象的stdin、stdout和stderr是三个文件句柄,可以像文件那样进行读写操作。

import subprocess
s = subprocess.Popen("python", stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=True)
s.stdin.write(b"import os\n")
s.stdin.write(b"print(os.environ)")
s.stdin.close()
可以对输入,输出交互性进行操作

============================================================================================================
"""
subprocess.run
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, timeout=None, check=False, encoding=None, errors=None)

功能:执行 args 参数所表示的命令,等待命令结束,并返回一个 CompletedProcess 类型对象。

注意,run() 方法返回的不是我们想要的执行结果或相关信息,而是一个 CompletedProcess 类型对象。

上面参数表里展示的只是一些常用的,真实情况还有很多。

args:表示要执行的命令,必须是一个字符串,字符串参数列表。

stdin、stdout 和 stderr:子进程的标准输入、输出和错误。其值可以是subprocess.PIPE、subprocess.DEVNULL、一个已经存在的文件描述符、已经打开的文件对象或者 None。

subprocess.PIPE表示为子进程创建新的管道,subprocess.DEVNULL表示使用os.devnull。默认使用的是 None,表示什么都不做。另外,stderr 可以合并到 stdout 里一起输出。

timeout:设置命令超时时间。如果命令执行时间超时,子进程将被杀死,并弹出TimeoutExpired异常。

check:如果该参数设置为 True,并且进程退出状态码不是 0,则弹出CalledProcessError异常。

encoding:如果指定了该参数,则 stdin、stdout 和 stderr 可以接收字符串数据,并以该编码方式编码。否则只接收 bytes 类型的数据。

shell:如果该参数为 True,将通过操作系统的 shell 执行指定的命令。

>>> subprocess.run(["ls", "-l"]) # 没有对输出进行捕获
CompletedProcess(args=['ls', '-l'], returncode=0)

>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

>>> subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE)
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n')

>>> subprocess.run("python --version", stdout=subprocess.PIPE)
CompletedProcess(args='python --version', returncode=0, stdout=b'Python 3.6.1\r\n')

>>>s= subprocess.run("ipconfig", stdout=subprocess.PIPE) # 捕获输出
>>>print(s.stdout.decode("GBK"))


subprocess.CompletedProcess
run() 方法的返回值,表示一个进程结束了。CompletedProcess类有下面这些属性:

args 启动进程的参数,通常是个列表或字符串。
returncode 进程结束状态返回码。0表示成功状态。
stdout 获取子进程的 stdout。通常为 bytes 类型序列,None 表示没有捕获值。如果你在调用 run() 方法时,设置了参数stderr=subprocess.STDOUT,则错误信息会和 stdout 一起输出,此时 stderr 的值是 None。
stderr 获取子进程的错误信息。通常为 bytes 类型序列,None 表示没有捕获值。
check_returncode() 用于检查返回码。如果返回状态码不为零,弹出CalledProcessError异常。
subprocess.DEVNULL
一个特殊值,用于传递给 stdout、stdin 和 stderr 参数。表示使用os.devnull作为参数值。

subprocess.PIPE
管道,可传递给 stdout、stdin 和 stderr 参数。

subprocess.STDOUT
特殊值,可传递给 stderr 参数,表示 stdout 和 stderr 合并输出。

args 与 shell
args 参数可以接收一个类似'du -sh'的字符串,也可以传递一个类似['du', '-sh']的字符串分割列表。shell 参数默认为 False,设置为 True 的时候表示使用操作系统的 shell 执行命令。


获取执行结果
run() 方法返回的是一个 CompletedProcess 类型对象,不能直接获取我们通常想要的结果。要获取命令执行的结果或者信息,在调用 run() 方法的时候,请指定 stdout=subprocess.PIPE。

"""


"""
交互式输入
并不是所有的操作系统命令都像‘dir’或者‘ipconfig’那样单纯地返回执行结果,还有很多像‘python’这种交互式的命令,你要输入点什么,然后它返回执行的结果。使用run()方法怎么向stdin里输入?

这样?

import subprocess

ret = subprocess.run("python", stdin=subprocess.PIPE, stdout=subprocess.PIPE,shell=True)
ret.stdin = "print('haha')" # 错误的用法
print(ret)
这样是不行的,ret作为一个CompletedProcess对象,根本没有stdin属性。那怎么办呢?前面说了,run()方法的stdin参数可以接收一个文件句柄。比如在一个1.txt文件中写入print('i like Python')。然后参考下面的使用方法:

import subprocess

fd = open("d:\\1.txt")
ret = subprocess.run("python", stdin=fd, stdout=subprocess.PIPE,shell=True)
print(ret.stdout)
fd.close()
这样做,虽然可以达到目的,但是很不方便,也不是以代码驱动的方式。这个时候,我们可以使用Popen类。

subprocess.Popen()
用法和参数与run()方法基本类同,但是它的返回值是一个Popen对象,而不是CompletedProcess对象。

>>> ret = subprocess.Popen("dir", shell=True)
>>> type(ret)
<class 'subprocess.Popen'>
>>> ret
<subprocess.Popen object at 0x0000000002B17668>
Popen对象的stdin、stdout和stderr是三个文件句柄,可以像文件那样进行读写操作。

>>>s = subprocess.Popen("ipconfig", stdout=subprocess.PIPE, shell=True)
>>>print(s.stdout.read().decode("GBK"))
要实现前面的‘python’命令功能,可以按下面的例子操作:

import subprocess

s = subprocess.Popen("python", stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=True)
s.stdin.write(b"import os\n")
s.stdin.write(b"print(os.environ)")
s.stdin.close()

out = s.stdout.read().decode("GBK")
s.stdout.close()
print(out)

"""

# https://zhuanlan.zhihu.com/p/91342640



import os
import subprocess
exe_path=os.path.abspath('./wxbot-sidecar.exe')

'''
subprocess.run是一个阻塞方法,执行了这个接口后,需要等待 run入参的命令执行完才能返回
'''
# result = subprocess.run(exe_path,shell=True,stdout=subprocess.PIPE)
# print(type(result))


'''
而有些时候,我们需要单独起一个(进程执行) cmd命令,然后周期性每几秒钟去检查命令执行的状态,
检查完之后我们还可以在主进程干别的事情,
也就是搞一个独立出来的进程。这种情况下, subprocess.run就无法满足,必须直接开 subprocess.Popen
'''
result = subprocess.Popen(exe_path,shell=True,stdout=subprocess.PIPE)
print(type(result))



'''
这段程序模拟了周期性等待子进程执行完成的场景,执行完成后拉取 stdout和 stderr打印,执行超时就强杀进程。基本上关键的地方都有注释,如果有其他类似的场景,可以直接照搬代码。

最后我们也能看到, subprocess本质也是多进程,但和 multiprocessing有所不同, multiprocessing是多个 python进程,着重于管理多个 python进程的运行时环境以及之间的通信;而 subprocess则是侧重于去跟踪 python程序启动的任意类型进程的状态。
两者也有共同点,就是主进程都会持有子进程的 handle,只要没调用类似 subprocess.run这种阻塞获取子进程状态/结果的接口,在起了新进程后,主进程内都能够随时随地去获取子进程的状态信息。

'''

import subprocess
import platform
import os
import signal

def _decode_bytes(_bytes):
encoding = 'gbk'
return _bytes.decode(encoding)

def _decode_stream(stream):
"""windows下解码stdout/stderr的数据"""
if not stream:
return ''
return _decode_bytes(stream.read())

args = ['ping', '127.0.0.1']
working_directory = '.'
wait_timeout = 1
cnt, maxcnt = 0, 4

print(f'platform system: {platform.system()}')
p = subprocess.Popen(args,
cwd=working_directory,

stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
print(f'process args: {args}')
print(f'process pid: {p.pid}')
while cnt < maxcnt:
try:
p.wait(timeout=wait_timeout)
except Exception as e:
print(f'attempt {cnt} -> wait err: {e}')
cnt += 1
finally:
if p.returncode is not None:
break

if p.returncode is None:
print('[ERROR] retcode is None, maybe timeout, try kill process...')
if platform.system() == 'Windows':
kill_proc_ret = subprocess.run(['taskkill', '/f', '/pid', str(p.pid)], capture_output=True)
print(f'[KILLPROC] {_decode_bytes(kill_proc_ret.stdout)}')
else:
os.kill(p.pid, signal.SIGKILL)
else:
retcode, stdout, stderr = p.returncode, _decode_stream(p.stdout), _decode_stream(p.stderr)
print(f'[OK] retcode: {retcode}\n\tstdout: {stdout}\n\tstderr: {stderr}')

标签:run,stdout,stdin,Popen,subprocess,PIPE,stderr
From: https://www.cnblogs.com/Zhouzg-2018/p/18201153

相关文章

  • Perform APK Downgrade Extraction on smartphones running Android 14
    SomeonementionedthatAPKDowngradeExtractionnolongerworksonAndroid14,andregardlessofthemobileforensictoolused,itisimpossibletosuccessfullyextractdataviaAPKDowngradeExtraction.I'mcuriousaboutthisissueanddecidetofind......
  • 执行npm run serve有时提示npm update check failed
    背景:这个错误虽说无关紧要,但有时候会出现就感觉不爽。错误提示: 解决方法:在网络上查阅资料后才知道是因为文件夹权限的问题(1.)删除目录configstore由于权限问题,该目录经常出现故障。如果删除该目录,则下次运行命令时将重新生成该目录。(2.)在Windows上删除......
  • Golang初学:获取程序内存使用情况,std runtime
    goversiongo1.22.1windows/amd64Windows11+amd64x86_64x86_64GNU/Linux--- 序章本文介绍golang程序占用内存的监控:使用stdruntime的ReadMemStats函数。 ReadMemStats函数https://pkg.go.dev/[email protected]//函数funcReadMemStats(m*MemStats......
  • openGauss lo_truncate
    lo_truncate功能描述将一个大对象截断成一个给定长度。原型intlo_truncate(PGconn*conn,intfd,size_tlen);参数表1lo_truncate参数关键字参数说明conn一个数据库连接fd文件描述符len要截断的长度返回值int:成功时返回0,失败时返回值为-1......
  • 【SpringBoot】实现项目启动后执行的两个接口ApplicationRunner和CommandLineRunner
    开发中可能会有这样的场景,需要在容器启动的时候执行一些内容。比如读取配置文件,数据库连接之类的。SpringBoot给我们提供了两个接口来帮助我们实现这种需求。两个启动加载接口分别是:CommandLineRunner和ApplicationRunner。Spring提供了接口InitializingBean,jdk提供了@PostCo......
  • skipped: maximum number of running instances reached (1)
    Python的 apscheduler今天出现skipped:maximumnumberofrunninginstancesreached(1)问题产生的原因:设置了大量的任务,而APScheduler无法同时处理所有任务解决方法:调整APScheduler使用的线程池大小来增加并发处理任务的能力fromapscheduler.schedulers......
  • Docker执行命令报错:Cannot connect to the Docker daemon at unix:///var/run/docker.
    1、问题说明Docker执行重新启动命令重启成功。命令如下:重新加载配置systemctldaemon-reload重启docker服务systemctlrestartdocker.service查看启动状态systemctlstatusdocker.service显示启动成功使用docker-v命令查看版本号输出正常但是使用dockerps......
  • 解释一下这两行 "pub": "pnpm --filter \"./packages/*\" run pub", "pub:b
    F:\learn-front\code-inspector\package.json这两行命令是用于在JavaScript项目中发布(publish)软件包到npm仓库的脚本定义,常见于使用pnpm作为包管理器的Monorepo(单仓库多项目)结构的项目中。这里具体解释一下每部分的含义:pub:这是一个npm脚本的别名,当在命令行中执行npmrunp......
  • trunk聚合
    Eth-Trunk又叫以太网链路聚合Eth-Trunk,它通过将多条以太网物理链路捆绑在一起成为一条逻辑链路。达到增加链路带宽的目的。在实现增大带宽目的的同时,Eth-Trunk采用备份链路的机制,可以有效的提高设备之间链路的可靠性。每个聚合组唯一对应着一个逻辑接口,这个逻辑接口称之为链路聚合......
  • Windows下使用ONNXRuntime推理YOLOv8
    一、准备工作将训练好的pt文件转为onnx格式。yoloexportmodel=best.ptformat=onnxdevice=0opset=13dynamic#如果是动态Shape的话,命令行参数dydynamic一定要加上,不然就是static的模型二、下载与安装ONNXRuntime注意:下载安装onnxruntime-gpu时需要保证其与cuda的兼容......