【问题标题】:Write command stdout to console and file (live) -- Python 3.5 + subprocess.run()将命令标准输出写入控制台和文件(实时)——Python 3.5 + subprocess.run()
【发布时间】: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 documentationsubprocess.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


【解决方案1】:

在尝试使用线程和轮询一段时间后,我无法让它正常工作。但是,从 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

【讨论】:

  • 你的依赖是什么?你是否以不寻常的方式进口东西?例如,它应该是 subprocess.Popen。之后它还说没有定义 STDOUT。
猜你喜欢
  • 1970-01-01
  • 2020-04-10
  • 1970-01-01
  • 2017-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-18
相关资源
最近更新 更多