【问题标题】:grep library output from within PythonPython 中的 grep 库输出
【发布时间】:2018-05-11 01:39:15
【问题描述】:

从命令行调用程序时,我可以将输出通过管道传输到grep 以选择我想查看的行,例如

printf "hello\ngood day\nfarewell\n" | grep day

我正在寻找相同类型的行选择,但寻找从 Python 调用的 C 库。考虑以下示例:

import os

# Function which emulate a C library call
def call_library():
    os.system('printf "hello\ngood day\nfarewell\n"')

# Pure Python stuff
print('hello from Python')
# C library stuff
call_library()

运行此 Python 代码时,我希望 C 部分的输出为字符串 'day'grep'ed,从而使代码输出

来自 Python 的你好
美好的一天

到目前为止,我已经使用herehere 描述的方法摆弄了stdout 的重定向。我能够使 C 输出完全消失,或者将其保存到 str 并稍后打印出来(这是两个链接主要关注的内容)。但是,我无法根据内容选择打印哪些行。重要的是,我希望在调用 C 库时实时输出,所以我不能只是将stdout 重定向到某个缓冲区并在事后对该缓冲区进行一些处理。

该解决方案只需要在 Linux 上使用 Python 3.x。如果除了行selection之外,该解决方案还可以进行行编辑,那就更好了。

我觉得应该可以,但是不知道怎么设置

  • stdout重定向到内存中的“文件”。

  • 生成一个新线程,该线程不断从该文件中读取,根据行内容进行选择,并将想要的行写入屏幕,即stdout 的原始目的地。

  • 调用 C 库

  • 将两个线程重新连接在一起并将stdout 重定向回其原始目标(屏幕)。

我对文件描述符之类的掌握不够牢固,无法做到这一点,甚至不知道这是否是最好的方法。

编辑

请注意,该解决方案不能简单地重新实现call_library 中的代码。代码必须调用call_library,与实际执行的代码完全无关。

【问题讨论】:

  • 是否有理由不能将所有输出捕获到 Python 字符串中,然后 split() 它并提取匹配行? (对于这种情况,大量的输出显然会破坏交易。)
  • 嗯,你会怎么做呢?请记住,我希望在调用库的 Python 会话中完成行选择。将所有输出通过管道传输到执行 grep'ing 的新 Python 会话不是一种选择。如果您认为自己有解决方案,请分享。
  • 您链接到的 Stack Overflow 问题有一个 StringIO 包装器,用于将标准输出捕获到变量中。
  • 是的,我确实可以让它发挥作用。但我怎样才能更进一步,实现我想要的呢?
  • 可以说,如果库不允许您将其结果捕获到内存缓冲区,则该库已损坏。顺便说一句,我遇到了这个模糊相关的问题:stackoverflow.com/questions/47381835/…

标签: python c linux python-3.x grep


【解决方案1】:

我对你的程序到底在做什么有点困惑,但听起来你有一个 C library 可以写入 C stdout不是 Python sys.stdout) 并且您想要捕获此输出并对其进行后处理,并且您已经拥有 C 库的 Python 绑定,您更愿意使用它而不是单独的 C 程序。

首先,您必须使用子进程来执行此操作;没有其他东西可以可靠地工作。这是因为stdout 是进程全局的,因此没有可靠的方法来捕获只有一个线程stdout 的写入。

其次,您可以使用subprocess.Popen,因为您可以使用它重新调用当前脚本!这就是 Python multiprocessing 模块在后台所做的事情,而且自己做起来并不难。我会使用一个特殊的隐藏命令行参数来区分孩子,如下所示:

import argparse
import subprocess
import sys

def subprocess_call_c_lib():
    import c_lib
    c_lib.do_stuff()

def invoke_c_lib():
    proc = subprocess.Popen([sys.executable, __file__,
                             "--internal-subprocess-call-c-lib"
                             # , ...
                             ],
                            stdin=subprocess.DEVNULL,
                            stdout=subprocess.PIPE)
    for line in proc.stdout:
        # filter output from the library here
        # to display to "screen", write to sys.stdout as usual

    if proc.wait():
        raise subprocess.CalledProcessError(proc.returncode, "c_lib")

def main():
    ap = argparse.Parser(...)
    ap.add_argument("--internal-subprocess-call-c-lib", action="store_true",
                    help=argparse.SUPPRESS)
    # ... more arguments ...

    args = ap.parse_args()
    if args.internal_subprocess_call_c_lib:
        subprocess_call_c_lib()
        sys.exit(0)

    # otherwise, proceed as before ...

main()

【讨论】:

  • 这行得通。但是,我需要 c_lib.do_stuff() 返回的对象(可能是几 GB i 内存)可用于原始进程。这可以实现吗?
  • 这是一个主要的额外并发症,您可能需要就此提出一个新问题。我通常建议使用pickle 或亲戚对其进行序列化,但如果它是几个 GB,那将不会有效。因此,如果可能的话,我首先要看的是将对该对象进行的任何工作都转移到子进程中。我要看的第二件事是以某种方式将该对象分配到共享内存段中。
【解决方案2】:

如果 grepping 线程至少打印到 stderr 是可能的:

# Function which emulate a C library call
def call_library():
    os.system("echo hello")
    time.sleep(1.0)
    os.system("echo good day")
    time.sleep(1.0)
    os.system("echo farewell")
    time.sleep(1.0)
    os.system("echo done")


class GrepThread(threading.Thread):
    def __init__(self, r,):
        threading.Thread.__init__(self)
        self.r = r

    def run(self):
        while True:
            s = self.r.readline()
            if not s:
                break
            if "day" in s:
                print(s, file=sys.stderr)    

original_stdout_fd = sys.stdout.fileno()
# file descriptors r, w for reading and writing
r, w = os.pipe() 
r = os.fdopen(r)
os.dup2(w, original_stdout_fd)
sys.stdout = io.TextIOWrapper(os.fdopen(original_stdout_fd, 'wb'))

thread = GrepThread(r)
thread.start()
print("Starting", file=sys.stderr)
call_library()

请注意,这不会关闭线程或清理内容,但它似乎可以在我的计算机上运行。它将在函数执行时打印行,而不是之后。

【讨论】:

  • 我希望将输出打印到stdout 而不是stderr,因为我对这两个流的处理方式不同。此外,程序似乎挂起,可能是因为未关闭的线程。你能解决这些问题吗?
  • 对于 Python 中的线程,只需在 SO 上搜索。不,不幸的是,我不知道如何将它打印到标准输出。
猜你喜欢
  • 1970-01-01
  • 2016-01-31
  • 1970-01-01
  • 2016-07-15
  • 1970-01-01
  • 1970-01-01
  • 2022-10-14
  • 2011-11-03
  • 1970-01-01
相关资源
最近更新 更多