Python将输入重定向到子进程

Python redirect input to child process

本文关键字:子进程 重定向 输入 Python      更新时间:2023-10-16

通常这不是什么大问题,因为通过STDIN/STDOUT传递数据很简单。

但我正在开发一个diff-util,它有两个输入和一个输出。

考虑:

diff <(curl 'http://google.com') <(curl 'https://google.com')
5c5
< <A HREF="http://www.google.com/">here</A>.
---
> <A HREF="https://www.google.com/">here</A>.

现在,使用普通的旧python程序就可以了,因为我可以open(sys.argv[1], 'r').read()来获得argv[1]和argv[2]的数据。

问题是,我的不同之处在于google_diff_match_patch的C++实现,为了简单起见,我正在调用该程序(该程序使用wifstreamwstringgetline读取其argv)。

所以现在必须要做的是,我必须把我的/dev/fd/11"给"我的subprocess.Popen(['dmp']),除了我似乎不能把路径(通常是)/dev/fd/11/dev/fd/12作为dmp C++程序的参数,因为它的/dev/fd/11不是我的python程序的/dev/fd/11

为了进一步混淆这个问题,我必须在将文件发送给孩子之前读出文件的内容,因为我使用file作为"is binary file"oracle:

file_process = Popen(['file', '-'], stdin=PIPE, stdout=PIPE)
file_content = open(filename, 'r').read()
(filetype, err) = file_process.communicate(file_content)
if filetype.find('text') == -1:
    # Popen my c++ program and try to feed it file_content

请不要给出"写入文件"之类的答案。我想实现这些输入重定向fifo,这样我就可以像使用任何其他命令行diff一样有效地使用该程序(例如,这包括curl在不保存到文件的情况下从网络上删除一些内容)。

编辑:根据subprocess,如果close_fds参数是False的默认值,则子级应该继承文件描述符。好吧,这似乎表明,如果在我的python包装程序中,我调用open('/dev/fd/11')而不关闭它,然后使用Popen()派生一个子级,那么该子级应该能够以某种方式读取文件描述符11。

好吧,既然我有了python的文件描述符11的内容,我该如何设置一个文件供孩子阅读?例如,如何复制<(echo file contents)的shell功能(不使用shell=Trueecho,我认为我现在应该这么做)

在我看来,你有一个外部可执行文件,它需要文件名作为参数,而你想从Python脚本中传递打开的文件描述符,对吗?这些文件描述符可能不是实际的文件,它们可能是stdin或其他管道?

如果是这样的话,你的工作就不容易了——应用程序需要的是文件名,而不是打开的文件。因为可执行文件的代码是按名称打开文件的,所以您无法从Python脚本中更改这种行为——即使可执行文件继承了文件描述符,其代码也需要考虑到这一假设。这并不是说可执行文件中的代码不能执行你的建议,只是不是为了执行而编写的。所以你试图做的是一个变通方法,不一定有一个干净的选项。

你说你不想要涉及写入文件的解决方案,但我觉得我应该指出,如果你只需要最少的工作,这确实是最简单的选择。如果您担心写入磁盘,那么您可以创建一个tmpfs分区或其他什么,但这会变得相当麻烦(而且不太便携)。

下一个最简单的方法可能是编写一个C扩展,它直接调用谷歌库,而不是使用第三方可执行文件——我想说,这比乱用/proc/self/fd或任何东西要干净得多(而且更便携)。事实上,只要检查了项目,它就已经提供了Python API,那么你有没有理由不直接调用它呢?就我个人而言,这绝对是我要采取的方法。

编辑: 啊,我刚刚发现Python API是纯Python,而不是C++模块的包装器,所以我想出于性能原因,您可能不会使用它。除非您有严格的性能要求,否则我仍然认为这是最简单的选择,但如果您真的需要C++性能,那么您仍然可以选择编写自己的包装器

如果您真的想调用可执行文件,而不想写入中间文件,那么我猜可以使用/dev/fd/*文件,但这可能只适用于真正的文件。至少在Linux上,这些文件是文件系统上底层文件的符号链接,因此,如果您的可执行文件通过符号链接重新打开它们,它应该在每个文件的开头获得一个读取指针,并且能够正确地进行diff。

然而,在stdin的情况下,您处理的是管道而不是真正的文件,所以我不相信这个技巧会奏效。如果您尝试,您将有两个具有相同底层管道的进程打开以供读取。这意味着管道的任何输出都将到达一个随机的子进程(不是完全随机的,但从您的角度来看是不可预测的)。现在,只要你的过程不是从stdin中读取的,那么你可能会逃脱惩罚,但这是一件非常可疑的事情。

简而言之,您可能只需打开/proc/self/fd文件(或者/dev/fd,如果您愿意的话)就可以了,但我不建议这样做。如果您正在使用的可执行文件没有以您想要的方式调用库,我建议直接调用库,要么编写您自己的Python C扩展包装器,要么使用现有的Python API。