使用python的subprocess模块遇到的问题及其解决

要求:使用python脚本,开启一个子进程并实时读取并解析其打印(printf())的字符串。

首先用linux shell脚本来做试验,

写一个echo-test.sh,循环输出,内容如下:

#!/bin/sh
while :;do
   echo "status:A"
   echo "status:B"
   echo "status:C"
   echo "status:D"
   echo "status:E"
   echo "status:V"    
   echo "status:F"
   sleep 1
done

单独执行echo-test.sh效果:

$ ./echo-test.sh
status:A
status:B
status:C
status:D
status:E
status:V
status:F
status:A
status:B
status:C
status:D
status:E
status:V
status:F
status:A
status:B
status:C
status:D
status:E
status:V
status:F
^C

再写一个python脚本rdstdout.py,读取并分析echo-test.sh的输出:
import time
from subprocess import  *
p=Popen("./echo-test.sh",shell=True,stdout=PIPE)
data=p.stdout.readline()
while  data:
    if "status:V" in data:
     print time.time()
     print data
    elif "status:A" in data:
     print time.time()
     print data
    data=p.stdout.readline()

执行下rdstdout.py,
$ python rdstdout.py
1517467252.54
status:A

1517467252.54
status:V

1517467253.54
status:A

1517467253.54
status:V

1517467254.54
status:A

1517467254.54
status:V

^CTraceback (most recent call last):
  File "rdstdout.py", line 14, in <module>
    data=p.stdout.readline()
KeyboardInterrupt

方法可行,初战告捷!

修改rdstdout.py,

p=Popen("./echo-test.sh",shell=True,stdout=PIPE)   --->

p=Popen("./gps_test.sh 9600",shell=True,stdout=PIPE)

gps_test 读取并解析gsp module输出的数据,定位成功输出status:A,未定位输出status:V.

单独执行gps_test,输出结果,

$ ./gps_test  9600

status:V

total sat:0

status:V

total sat:0

status:V

total sat:0

^C

但执行rdstdout.py,看不到任何输出,按键盘退出程序才看到输出信息。

修改p=Popen("./gps_test.sh 9600",shell=True,stdout=PIPE) --->

p=Popen("ping www.baidu.com",shell=True,stdout=PIPE),

可以即时获取ping输出的数据。怀疑到printf()有个缓冲,需要在其后加一句fflush(stdout); 这样修改编译出新的gps_test,执行python  rdstdout.py可以即时获取gps_test输出的数据,

执行 objdump -T /bin/ping |grep fflush
0000000000000000      DF *UND*    0000000000000000  GLIBC_2.2.5 fflush
可以看到ping程序也用到了fflush,应该也是用了类似方法吧。不过需要在每个线程的printf()后都要加上fflush(stdout),比较繁琐,可以在执行主线程printf()前加一句setbuf(stdout, NULL),  stdout就没有buffer可用,每个线程的printf()数据都可以即时获取。

当然也可以用fprintf(stderr,... 代替printf(),也可以解决问题。

这些办法的共同点就是都需要修改gps_test的源码,如果没有源码,这些办法就不好用了,可以考虑用API hook的方法,具体就不在这里讨论了。




### Python `subprocess` 模块详解 #### 功能概述 `subprocess` 模块用于生成新的进程,连接到它们的输入/输出/错误管道,并获取返回的状态码。相比旧的方法如 `os.system()` 更加灵活和强大[^3]。 #### 主要优势 - 可以获得子进程的标准输出和标准错误流。 - 提供更好的错误处理机制。 - 支持复杂的子进程操作,比如设置环境变量、改变工作目录等。 - 替代多个老旧模块和方法,统一接口设计[^4]。 #### 常见函数介绍 ##### 1. `subprocess.run()` 自 Python 3.5 版本引入,推荐作为主要API使用。此函数执行给定命令并等待完成,然后返回一个包含结果信息的对象 (`CompletedProcess`)。可以通过传递不同参数控制行为: ```python import subprocess result = subprocess.run(['ls', '-l'], capture_output=True, text=True) print(f"Return code: {result.returncode}") if result.stdout: print('Have stdout:') print(result.stdout) else: print('No output.') ``` ##### 参数说明: - **args**: 执行的命令及其参数,通常为列表形式; - **capture_output**: 是否捕捉标准输出和标准错误,默认 False; - **text**: 若设为 True 则将字节串转换成字符串; - **check**: 当命令失败时抛出异常; - **timeout**: 设置超时时长; ##### 2. `subprocess.call()` 简单地运行指定命令而不关心其输出内容,只关注最终退出状态。适用于不需要进一步处理输出的情况。 ```python ret_code = subprocess.call(["echo", "Hello world"]) print(ret_code) # 输出应为0表示成功 ``` ##### 3. `subprocess.check_call()` 类似于 `call()` 函数但是会在遇到非零退出状态时报错。 ```python try: ret_code = subprocess.check_call(["false"]) # 此处故意触发错误 except subprocess.CalledProcessError as e: print(e) ``` ##### 4. `subprocess.check_output()` 专门用来收集命令产生的输出数据,如果命令失败也会报错。 ```python output = subprocess.check_output(["echo", "hello"], universal_newlines=True) print(output.strip()) ``` #### 实际应用场景举例 假设有一个需求是要批量压缩文件夹内的图片资源,在 Linux 下可以借助 ImageMagick 工具实现这一目标。下面是一段简单的脚本来展示如何利用 `subprocess` 来调用外部程序 mogrify 对图像进行缩放处理: ```bash #!/usr/bin/env python3 import os from pathlib import Path import subprocess def resize_images(directory_path, width=800): """Resize all images within a directory to specified maximum width.""" image_files = list(Path(directory_path).glob('*.{jpg,jpeg,png,gif}')) for img_file in image_files: try: cmd = ['mogrify', '-resize', f'{width}>', str(img_file)] completed_process = subprocess.run(cmd, check=True, timeout=60) except (FileNotFoundError, PermissionError) as err: print(f'Failed to execute command due to error: {err}') except subprocess.TimeoutExpired: print(f'Timeout occurred while processing file "{img_file}"') else: print(f'Successfully resized "{img_file.name}"') if __name__ == '__main__': dir_to_resize = '/path/to/images' if not os.path.isdir(dir_to_resize): raise NotADirectoryError(f'Directory does not exist or is invalid: {dir_to_resize}') resize_images(dir_to_resize) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值