将命令标准输出写入控制台和文件(实时)——Python 3.5 + subprocess.run()
Write command stdout to console and file (live) -- Python 3.5 + subprocess.run()
平台:Python rhel6(64 位)上的 3.5
场景:执行一个Bash命令,其中运行是一个作业。这项工作 returns 几行输出到标准输出,每隔几秒。
命令:./run_job --name 'myjob' --config_file ./myconfig.conf
目标:使用Python的subprocess.run()
,我正在尝试运行上述命令并捕获进程的标准输出,将其打印到控制台并将其保存到文件中。我需要在 stdout 可用时打印它(实时)。
我试过的:我对此进行了广泛的搜索,我找到的每个解决方案都使用了 subprocess.Popen()
。 This method somewhat worked, but implementing it resulted in breaking the return logic I currently have. Reading through the 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.")
这部分有效,但只有在进程完成时才会打印完整的标准输出。所以终端将挂起直到发生这种情况。我需要以实时方式逐行打印 stdout,以便它可以由记录器写入。
有什么方法可以在不修改现有代码的情况下做到这一点吗?如有任何帮助,我们将不胜感激。
在尝试使用线程和轮询一段时间后,我无法让它正常工作。然而,从 Todd 链接的同一个线程中,我找到了另一种方法来实现我需要的,使用 Popen
:
def run_shell(cmd_str, print_stdout=True, fail_msg=""):
"""Run a Linux shell command and return the result code."""
p = Popen(cmd_str,
shell=True,
stderr=STDOUT,
stdout=PIPE,
bufsize=1,
universal_newlines=True)
logger = logging.getLogger()
output_lines = list()
while True:
output = p.stdout.readline().strip()
if len(output) == 0 and p.poll() is not None:
break
output_lines.append(output)
if print_stdout:
logger.info("[cmd] " + output)
cmd_code = p.returncode
cmd_stdout = "\n".join(output_lines)
if cmd_code != 0 and fail_msg != "":
logger.error(fail_msg)
exit(-1)
return cmd_code, cmd_stdout
平台:Python rhel6(64 位)上的 3.5
场景:执行一个Bash命令,其中运行是一个作业。这项工作 returns 几行输出到标准输出,每隔几秒。
命令:./run_job --name 'myjob' --config_file ./myconfig.conf
目标:使用Python的subprocess.run()
,我正在尝试运行上述命令并捕获进程的标准输出,将其打印到控制台并将其保存到文件中。我需要在 stdout 可用时打印它(实时)。
我试过的:我对此进行了广泛的搜索,我找到的每个解决方案都使用了 subprocess.Popen()
。 This method somewhat worked, but implementing it resulted in breaking the return logic I currently have. Reading through the 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.")
这部分有效,但只有在进程完成时才会打印完整的标准输出。所以终端将挂起直到发生这种情况。我需要以实时方式逐行打印 stdout,以便它可以由记录器写入。
有什么方法可以在不修改现有代码的情况下做到这一点吗?如有任何帮助,我们将不胜感激。
在尝试使用线程和轮询一段时间后,我无法让它正常工作。然而,从 Todd 链接的同一个线程中,我找到了另一种方法来实现我需要的,使用 Popen
:
def run_shell(cmd_str, print_stdout=True, fail_msg=""):
"""Run a Linux shell command and return the result code."""
p = Popen(cmd_str,
shell=True,
stderr=STDOUT,
stdout=PIPE,
bufsize=1,
universal_newlines=True)
logger = logging.getLogger()
output_lines = list()
while True:
output = p.stdout.readline().strip()
if len(output) == 0 and p.poll() is not None:
break
output_lines.append(output)
if print_stdout:
logger.info("[cmd] " + output)
cmd_code = p.returncode
cmd_stdout = "\n".join(output_lines)
if cmd_code != 0 and fail_msg != "":
logger.error(fail_msg)
exit(-1)
return cmd_code, cmd_stdout