【发布时间】:2020-03-19 20:37:59
【问题描述】:
平台:rhel6(64 位)上的 Python 3.5
场景:执行一个运行作业的 Bash 命令。该作业每隔几秒将几行输出返回到标准输出。
命令:./run_job --name 'myjob' --config_file ./myconfig.conf
目标:使用 Python 的 subprocess.run(),我正在尝试运行上述命令并捕获进程的标准输出,将其打印到控制台并将其保存到文件中。我需要在标准输出可用(实时)时打印它。
我的尝试:我为此进行了广泛的搜索,我找到的每个解决方案都使用subprocess.Popen()。 This method 有点工作,但实施它导致破坏了我目前拥有的返回逻辑。通读Python documentation,subprocess.run() 方法是 Python 3.5 的推荐方法,这就是我走这条路的原因。
我的设置:到目前为止,我有一个通用文件,其中包含日志记录并运行下面的 shell 命令。
def setup_logging(log_lvl="INFO"):
script_name = path.splitext(path.basename(__file__))[0]
log_path = environ["HOME"] + "/logs/" + script_name + ".log"
logging.basicConfig(
level=getattr(logging, log_lvl.upper()),
format="%(asctime)s: [%(levelname)s] %(message)s",
handlers=[
logging.FileHandler(filename=log_path, mode="w", encoding="utf-8"),
logging.StreamHandler()
]
)
def run_shell(cmd_str, print_stdout=True, fail_msg=""):
logger = logging.getLogger()
result = run(cmd_str, universal_newlines=True, shell=True, stderr=STDOUT, stdout=PIPE)
cmd_stdout = result.stdout.strip()
cmd_code = result.returncode
if print_stdout and cmd_stdout != "":
logger.info("[OUT] " + cmd_stdout)
if cmd_code != 0 and fail_msg != "":
logger.error(fail_msg)
exit(cmd_code)
return cmd_code, cmd_stdout
所以我会使用以下代码来运行我的脚本:
run_shell("./run_job --name 'myjob' --config_file ./myconfig.conf", fail_msg="Job failed.")
这部分有效,但只有在该过程完成后才会打印完整的标准输出。所以终端将挂起,直到发生这种情况。我需要以实时的方式逐行打印标准输出,以便记录器可以写入。
有什么方法可以在不彻底检查现有代码的情况下做到这一点?任何帮助将不胜感激。
【问题讨论】:
-
我最近和另一个 SO 用户一起与另一个子流程交互:stackoverflow.com/a/60680949/7915759 这个例子可以应用在这个场景中。您可以从 python 脚本中读回标准输出。
-
在该示例中,可以修改线程以将其捕获的每一行写入文件,或者您可以在主循环中执行此操作。或者只是使主循环与线程相同的算法并摆脱线程和队列。
-
对于
subprocess.run(),我相信您必须等到它完成并立即获得所有输出。使用我链接的解决方案,您可以捕获输入的输出。因此,run()无法实现您所描述的实时捕获要求。 -
@Todd:感谢您的链接!我会看看。我也有一种感觉,
subprocess.run()无法实现。如果我可以让它与你提供的东西一起工作,我会在这里发回。
标签: python logging subprocess python-3.5