
在Python脚本中执行外部命令是常见的操作,通常使用内置的subprocess模块。然而,当需要对子进程的每一行输出都加上时间戳时,事情变得复杂。传统的Unix shell方法,如 command | while IFS= read -r line; do printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$line"; done,在subprocess中直接通过管道连接并不直观或容易实现,因为Python需要自身来控制输出流,而不是依赖shell的管道逻辑。直接将整个shell命令字符串传递给subprocess.Popen并设置shell=True虽然可行,但通常不推荐,因为它可能引入安全风险且难以精确控制。
为了优雅地解决这个问题,我们可以利用pexpect库来模拟终端交互,逐行读取子进程的输出,并结合Python强大的logging模块来自动为每行输出添加时间戳及其他日志信息。
pexpect是一个Python模块,用于控制其他程序,模拟终端交互。它可以启动子进程,然后像用户在终端中一样发送命令、读取输出、等待特定模式出现。这使得它非常适合捕获子进程的实时输出。
Python的logging模块是处理程序日志的标准库。它提供了灵活的日志记录功能,包括不同的日志级别(DEBUG, INFO, WARNING, ERROR, CRITICAL)、多种处理器(文件、控制台、网络等)以及高度可定制的日志格式。利用logging模块,我们可以轻松地在每条日志消息前自动添加时间戳。
立即学习“Python免费学习笔记(深入)”;
以下是实现子进程输出时间戳化的核心代码示例:
#! /usr/bin/env python
import logging
import pexpect
import sys
# 1. 配置日志系统
# 设置日志文件、编码、输出格式和最低日志级别
# %(asctime)s 会自动插入时间戳
# %(levelname)-8s 会插入日志级别,并左对齐,占用8个字符
# %(message)s 是实际的日志消息
logging.basicConfig(
filename='subprocess_output.log', # 日志输出到文件
encoding='utf-8',
format='%(asctime)s %(levelname)-8s %(message)s',
level=logging.INFO # 设定日志级别为INFO,DEBUG级别会输出更多信息
)
# 2. 定义一个函数来执行命令并记录输出
def run_command_with_timestamp_logging(cmd: str):
"""
执行给定的shell命令,并将其标准输出逐行记录到日志中,
每行自动带有时间戳。
"""
logging.info(f"Executing command: '{cmd}'") # 记录执行的命令
try:
# 使用pexpect.spawn启动命令
# encoding="utf-8" 确保正确处理字符编码
p = pexpect.spawn(cmd, encoding="utf-8")
# 循环读取子进程的每一行输出
# p.readline() 会读取一行直到遇到换行符,并包含换行符
# 赋值表达式 (:=) 允许在while条件中赋值
while line := p.readline():
# 使用logging.info记录每一行输出
# .strip() 用于移除行尾的换行符和空白符,使日志更整洁
logging.info(line.strip())
# 等待子进程结束并获取退出状态码
p.wait()
if p.exitstatus is not None and p.exitstatus != 0:
logging.error(f"Command '{cmd}' exited with status {p.exitstatus}")
logging.error(f"Before: {p.before.strip()}") # 记录进程退出前的输出
logging.error(f"After: {p.after.strip()}") # 记录进程退出后的输出
elif p.exitstatus == 0:
logging.info(f"Command '{cmd}' completed successfully.")
except pexpect.exceptions.TIMEOUT:
logging.error(f"Command '{cmd}' timed out.")
except pexpect.exceptions.EOF:
logging.error(f"Command '{cmd}' reached EOF unexpectedly.")
except Exception as e:
logging.critical(f"An unexpected error occurred: {e}")
# 3. 示例用法
if __name__ == "__main__":
print("Running commands and logging output to 'subprocess_output.log'...")
# 运行一个简单的ls命令
run_command_with_timestamp_logging("ls -l")
print("\n--- Running a multi-line output command ---")
# 运行一个会产生多行输出的命令
run_command_with_timestamp_logging("docker build .") # 假设docker已安装并有Dockerfile
print("\n--- Running a command with error output ---")
# 运行一个会产生错误输出的命令
run_command_with_timestamp_logging("non_existent_command")
print("\nCheck 'subprocess_output.log' for detailed output.")日志配置 (logging.basicConfig):
run_command_with_timestamp_logging 函数:
通过巧妙地结合pexpect库的交互式进程控制能力和Python logging模块强大的日志格式化功能,我们可以轻松实现对Python子进程输出的逐行时间戳化。这种方法不仅解决了subprocess模块在处理复杂shell管道时的局限性,还提供了一种专业、可扩展且易于维护的日志记录方案,极大地提升了脚本的可观测性和调试效率。
以上就是使用pexpect和logging为Python子进程输出添加时间戳的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号