Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/362.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby-on-rails-4/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在使用子流程时,如何在Python中复制tee行为?_Python_Subprocess_Stdout_Stderr_Tee - Fatal编程技术网

在使用子流程时,如何在Python中复制tee行为?

在使用子流程时,如何在Python中复制tee行为?,python,subprocess,stdout,stderr,tee,Python,Subprocess,Stdout,Stderr,Tee,我正在寻找一个Python解决方案,它允许我将命令的输出保存在一个文件中,而不必在控制台中隐藏它 仅供参考:我问的是(作为Unix命令行实用程序)而不是Python intertools模块中同名的函数 细节 Python解决方案(不调用tee,在Windows下不可用) 我不需要为调用的进程向stdin提供任何输入 我无法控制被调用的程序。我只知道它将输出一些东西到stdout和stderr,并返回一个退出代码 调用外部程序(子进程)时工作的步骤 为stderr和stdout 能够区分std

我正在寻找一个Python解决方案,它允许我将命令的输出保存在一个文件中,而不必在控制台中隐藏它

仅供参考:我问的是(作为Unix命令行实用程序)而不是Python intertools模块中同名的函数

细节
  • Python解决方案(不调用
    tee
    ,在Windows下不可用)
  • 我不需要为调用的进程向stdin提供任何输入
  • 我无法控制被调用的程序。我只知道它将输出一些东西到stdout和stderr,并返回一个退出代码
  • 调用外部程序(子进程)时工作的步骤
  • stderr
    stdout
  • 能够区分stdout和stderr,因为我可能只想向控制台显示其中一个,或者我可以尝试使用不同的颜色输出stderr-这意味着
    stderr=subprocess.stdout
    将无法工作
  • 实时输出(渐进式)-进程可以运行很长时间,我不能等待它完成
  • Python 3兼容代码(重要)
工具书类 以下是我目前发现的一些不完整的解决方案:

  • (mkfifo仅在Unix上工作)
  • (根本不起作用)

当前代码(第二次尝试) 实际产出 预期的输出是订购生产线。注意,修改Popen以仅使用一个管道是不允许的,因为在现实生活中,我想用stderr和stdout做不同的事情


即使在第二种情况下,我也无法获得实时的like out,事实上,所有结果都是在流程完成时收到的。默认情况下,Popen不应使用缓冲区(bufsize=0)。

这是Python的一个直接端口

import sys
sinks = sys.argv[1:]
sinks = [open(sink, "w") for sink in sinks]
sinks.append(sys.stderr)
while True:
  input = sys.stdin.read(1024)
  if input:
    for sink in sinks:
      sink.write(input)
  else:
    break
我现在正在Linux上运行,但这应该在大多数平台上都能运行


现在对于
子流程
部分,我不知道您想如何将子流程的
stdin
stdout
stderr
连接到您的
stdin
stdout
stderr
和文件接收器,但我知道您可以做到这一点:

import subprocess
callee = subprocess.Popen( ["python", "-i"],
                           stdin = subprocess.PIPE,
                           stdout = subprocess.PIPE,
                           stderr = subprocess.PIPE
                         )
现在,您可以像访问普通文件一样访问这些文件,从而使上述“解决方案”能够正常工作。如果你想拿到电话,你需要打一个额外的电话到


callee.stdin时要小心:如果您这样做时进程已退出,则可能会出现错误(在Linux上,我得到
IOError:[Errno 32]断管
)。

如果您不想与进程交互,可以很好地使用子进程模块

例如:

tester.py

import os
import sys

for file in os.listdir('.'):
    print file

sys.stderr.write("Oh noes, a shrubbery!")
sys.stderr.flush()
sys.stderr.close()
测试.py

import subprocess

p = subprocess.Popen(['python', 'tester.py'], stdout=subprocess.PIPE,
                     stdin=subprocess.PIPE, stderr=subprocess.PIPE)

stdout, stderr = p.communicate()
print stdout, stderr

在您的情况下,只需先将stdout/stderr写入文件即可。您也可以使用communicate向流程发送参数,尽管我不知道如何与子流程持续交互。

我看到这是一篇相当古老的帖子,但只是为了防止有人仍在寻找这样做的方法:

proc = subprocess.Popen(["ping", "localhost"], 
                        stdout=subprocess.PIPE, 
                        stderr=subprocess.PIPE)

with open("logfile.txt", "w") as log_file:
  while proc.poll() is None:
     line = proc.stderr.readline()
     if line:
        print "err: " + line.strip()
        log_file.write(line)
     line = proc.stdout.readline()
     if line:
        print "out: " + line.strip()
        log_file.write(line)

我的解决方案并不优雅,但很有效

您可以使用powershell访问WinOS下的“tee”

import subprocess
import sys

cmd = ['powershell', 'ping', 'google.com', '|', 'tee', '-a', 'log.txt']

if 'darwin' in sys.platform:
    cmd.remove('powershell')

p = subprocess.Popen(cmd)
p.wait()

这就是可以做到的

import sys
from subprocess import Popen, PIPE

with open('log.log', 'w') as log:
    proc = Popen(["ping", "google.com"], stdout=PIPE, encoding='utf-8')
    while proc.poll() is None:
        text = proc.stdout.readline() 
        log.write(text)
        sys.stdout.write(text)

如果要求Python3.6不是一个问题,那么现在有一种方法可以使用asyncio实现这一点。此方法允许您分别捕获stdout和stderr,但仍然可以在不使用线程的情况下将这两个流都传输到tty。这里有一个大致的轮廓:

class RunOutput():
    def __init__(self, returncode, stdout, stderr):
        self.returncode = returncode
        self.stdout = stdout
        self.stderr = stderr

async def _read_stream(stream, callback):
    while True:
        line = await stream.readline()
        if line:
            callback(line)
        else:
            break

async def _stream_subprocess(cmd, stdin=None, quiet=False, echo=False) -> RunOutput:
    if isWindows():
        platform_settings = {'env': os.environ}
    else:
        platform_settings = {'executable': '/bin/bash'}

    if echo:
        print(cmd)

    p = await asyncio.create_subprocess_shell(cmd,
                                              stdin=stdin,
                                              stdout=asyncio.subprocess.PIPE,
                                              stderr=asyncio.subprocess.PIPE,
                                              **platform_settings)
    out = []
    err = []

    def tee(line, sink, pipe, label=""):
        line = line.decode('utf-8').rstrip()
        sink.append(line)
        if not quiet:
            print(label, line, file=pipe)

    await asyncio.wait([
        _read_stream(p.stdout, lambda l: tee(l, out, sys.stdout)),
        _read_stream(p.stderr, lambda l: tee(l, err, sys.stderr, label="ERR:")),
    ])

    return RunOutput(await p.wait(), out, err)


def run(cmd, stdin=None, quiet=False, echo=False) -> RunOutput:
    loop = asyncio.get_event_loop()
    result = loop.run_until_complete(
        _stream_subprocess(cmd, stdin=stdin, quiet=quiet, echo=echo)
    )

    return result


上面的代码基于这篇博文:

这在Linux中是次优的,因为Linux提供了一个特殊的API,但这不是重点,对吗?我更新了问题,问题是,我无法找到如何使用子流程,以便逐渐从两个管道获取数据,而不是在流程结束时一次获取所有数据。我知道您的代码应该可以工作,但有一个小要求确实打破了整个逻辑:我希望能够区分stdout和stderr,这意味着我我必须阅读他们两个,但我不知道谁会得到新的数据。请看一下示例代码。@Sorin,这意味着您必须使用两个线程。一个在标准上读,一个在标准上读。如果要将两者写入同一个文件,则可以在开始读取时获取接收器上的锁,并在写入行终止符后释放该锁:/对我来说,使用线程并不太吸引人,也许我们会找到其他东西。奇怪的是,这是一个常见的问题,但没有人提供完整的解决方案。这并没有在STDOUT上下文中显示STDERR中的错误消息,这会使调试shell脚本等几乎不可能。意思是。。。?在这个脚本中,通过STDERR传递的任何内容都将与STDOUT一起打印到屏幕上。如果您指的是返回代码,只需使用
p.poll()
检索它们。这不满足“渐进”条件。这对我很有效,尽管我发现
stdout,stderr=proc.communicate()
更易于使用。-1:此解决方案会导致任何子进程出现死锁,这些子进程可以在stdout或stderr上生成足够的输出,并且stdout/stderr不能完全同步。@J.F.Sebastian:是的,但是您可以通过将
readline()
替换为
readline(size)
来解决此问题。我在其他语言中也做过类似的事情。裁判:@kevinarpe错了
readline(大小)
无法修复死锁。stdout/stderr应该同时读取。请参阅“显示使用线程或asyncio的解决方案”问题下的链接。@J.F.sebastiando如果我只对其中一个流感兴趣,是否存在此问题?相关:相关:这种方式可能重复投票,因为这是一个社区wiki:-)如果有人想知道,可以使用
print()
而不是
sys.stdout.write()
:-)@progyamer
print
将添加一个额外的换行符,当您需要忠实地复制输出时,它不是您想要的。是的,但是
print(line,
import sys
from subprocess import Popen, PIPE

with open('log.log', 'w') as log:
    proc = Popen(["ping", "google.com"], stdout=PIPE, encoding='utf-8')
    while proc.poll() is None:
        text = proc.stdout.readline() 
        log.write(text)
        sys.stdout.write(text)
class RunOutput():
    def __init__(self, returncode, stdout, stderr):
        self.returncode = returncode
        self.stdout = stdout
        self.stderr = stderr

async def _read_stream(stream, callback):
    while True:
        line = await stream.readline()
        if line:
            callback(line)
        else:
            break

async def _stream_subprocess(cmd, stdin=None, quiet=False, echo=False) -> RunOutput:
    if isWindows():
        platform_settings = {'env': os.environ}
    else:
        platform_settings = {'executable': '/bin/bash'}

    if echo:
        print(cmd)

    p = await asyncio.create_subprocess_shell(cmd,
                                              stdin=stdin,
                                              stdout=asyncio.subprocess.PIPE,
                                              stderr=asyncio.subprocess.PIPE,
                                              **platform_settings)
    out = []
    err = []

    def tee(line, sink, pipe, label=""):
        line = line.decode('utf-8').rstrip()
        sink.append(line)
        if not quiet:
            print(label, line, file=pipe)

    await asyncio.wait([
        _read_stream(p.stdout, lambda l: tee(l, out, sys.stdout)),
        _read_stream(p.stderr, lambda l: tee(l, err, sys.stderr, label="ERR:")),
    ])

    return RunOutput(await p.wait(), out, err)


def run(cmd, stdin=None, quiet=False, echo=False) -> RunOutput:
    loop = asyncio.get_event_loop()
    result = loop.run_until_complete(
        _stream_subprocess(cmd, stdin=stdin, quiet=quiet, echo=echo)
    )

    return result