首页 文章

Python Popen:同时写入stdout和日志文件

提问于
浏览
30

我正在使用Popen调用一个shell脚本,该脚本不断将其stdout和stderr写入日志文件 . 有没有办法连续同时输出日志文件(到屏幕),或者让shell脚本同时写入日志文件和标准输出?

我基本上想在Python中做这样的事情:

cat file 2>&1 | tee -a logfile #"cat file" will be replaced with some script

再次,这将stderr / stdout连接到tee,将它写入stdout和我的日志文件 .

我知道如何在Python中将stdout和stderr写入日志文件 . 我被困在哪里是如何将这些复制回屏幕:

subprocess.Popen("cat file", shell=True, stdout=logfile, stderr=logfile)

当然我可以做这样的事情,但有没有办法在没有tee和shell文件描述符重定向的情况下做到这一点?:

subprocess.Popen("cat file 2>&1 | tee -a logfile", shell=True)

3 回答

  • 9

    您可以使用管道从程序的标准输出读取数据并将其写入您想要的所有位置:

    import sys
    import subprocess
    
    logfile = open('logfile', 'w')
    proc=subprocess.Popen(['cat', 'file'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    for line in proc.stdout:
        sys.stdout.write(line)
        logfile.write(line)
    proc.wait()
    

    UPDATE

    在python 3中, universal_newlines 参数控制管道的使用方式 . 如果 False ,管道读取返回 bytes 对象并且可能需要被解码(例如, line.decode('utf-8') )以获得字符串 . 如果 True ,python会为你解码

    版本3.3中更改:当universal_newlines为True时,类使用编码locale.getpreferredencoding(False)而不是locale.getpreferredencoding() . 有关此更改的更多信息,请参阅io.TextIOWrapper类 .

  • 30

    要模拟: subprocess.call("command 2>&1 | tee -a logfile", shell=True) 而不调用 tee 命令:

    #!/usr/bin/env python2
    from subprocess import Popen, PIPE, STDOUT
    
    p = Popen("command", stdout=PIPE, stderr=STDOUT, bufsize=1)
    with p.stdout, open('logfile', 'ab') as file:
        for line in iter(p.stdout.readline, b''):
            print line,  #NOTE: the comma prevents duplicate newlines (softspace hack)
            file.write(line)
    p.wait()
    

    要修复可能的缓冲问题(如果输出延迟),请参阅Python: read streaming input from subprocess.communicate()中的链接 .

    这是Python 3版本:

    #!/usr/bin/env python3
    import sys
    from subprocess import Popen, PIPE, STDOUT
    
    with Popen("command", stdout=PIPE, stderr=STDOUT, bufsize=1) as p, \
         open('logfile', 'ab') as file:
        for line in p.stdout: # b'\n'-separated lines
            sys.stdout.buffer.write(line) # pass bytes as is
            file.write(line)
    
  • 0

    Write to terminal byte by byte for interactive applications

    此方法立即将其获取的任何字节写入stdout,这更接近地模拟了 tee 的行为,尤其是对于交互式应用程序 .

    main.py

    #!/usr/bin/env python3
    import os
    import subprocess
    import sys
    with subprocess.Popen(sys.argv[1:], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) as proc, \
            open('logfile.txt', 'bw') as logfile:
        while True:
            byte = proc.stdout.read(1)
            if byte:
                sys.stdout.buffer.write(byte)
                sys.stdout.flush()
                logfile.write(byte)
                # logfile.flush()
            else:
                break
    exit_status = proc.returncode
    

    sleep.py

    #!/usr/bin/env python3
    import sys
    import time
    for i in range(10):
        print(i)
        sys.stdout.flush()
        time.sleep(1)
    

    首先,我们可以进行非交互式健全性检查:

    ./main.py ./sleep.py
    

    而且我们认为这是实时的stdout .

    接下来,对于交互式测试,您可以运行:

    ./main.py bash
    

    然后,您键入的字符会在您键入时立即显示在终端上,这对于交互式应用程序非常重要 . 运行时会发生这种情况:

    bash | tee logfile.txt
    

    此外,如果您希望输出立即显示在ouptut文件上,那么您还可以添加:

    logfile.flush()
    

    tee 不会这样做,我担心这会扼杀性能 . 您可以使用以下方法轻松测试:

    tail -f logfile.txt
    

    相关问题:live output from subprocess command

    在Ubuntu 18.04,Python 3.6.7上测试 .

相关问题