Python将输入重定向到子进程

时间:2013-06-30 17:57:18

标签: c++ python unix file-io

通常这不是一个问题,因为通过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 ++实现,为了简单起见,我正在调用该程序(使用argvwifstream读取其wstring,和getline)。

所以现在必须发生的事情是我必须“给”/dev/fd/11subprocess.Popen(['dmp'])给我/dev/fd/11,除了我似乎无法填充路径(通常是){{1 }和/dev/fd/12作为dmp C ++程序的参数,因为它的/dev/fd/11不是我的python程序的/dev/fd/11

为了进一步混淆问题,我必须在将文件发送给孩子之前将其读出来,因为我使用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,我知道我现在应该这样做了)

1 个答案:

答案 0 :(得分:1)

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

如果是这种情况,那么你就不会有轻松的工作 - 应用程序需要文件名,而不是打开文件。因为该可执行文件的代码是按名称打开文件,所以您无法从Python脚本中更改该行为 - 即使可执行文件继承了文件描述符,其代码也需要在编写时考虑到该假设。并不是该可执行文件中的代码不能执行您所建议的操作,只是它不是编写来执行此操作。所以你试图做的是一种解决方法,并不一定有一个干净的选择。

你说你不想要涉及写文件的解决方案,但我觉得我应该指出,如果你是在最小的工作之后,那真的是最简单的选择。如果您担心写入磁盘,那么您可以创建一个tmpfs分区或其他东西,但这样做得相当繁琐(而且不是很便携)。

下一个最简单的可能就是编写一个C扩展名,它直接调用Google库而不是使用第三方可执行文件 - 我会说这比使用/proc/self/fd或者/dev/fd/*更加清晰(更便携)任何东西。事实上,只是检查了the project它已经提供了一个Python API,所以你有没有理由不直接调用它?就个人而言,这绝对是我要采取的方法。

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

如果您真的打算调用可执行文件而不打算写入中间文件,那么我猜你可以使用stdin文件,但这可能是仅适用于真实文件。至少在Linux上,这些文件作为文件系统底层文件的符号链接,所以如果你的可执行文件通过符号链接重新打开它,它应该在每个文件的开头都得到一个读指针,并且能够正确地做差异。

但是在stdin的情况下,你处理的是管道而不是真正的文件,所以我不相信这个技巧会起作用。如果您尝试它,您将有两个进程打开相同的底层管道进行读取。这意味着管道的任何输出都将到达一个随机的子进程(不是完全随机的,但从您的角度来看是不可预测的)。现在,只要你的流程没有从/proc/self/fd读取,那么你可能会逃避这一点,但这是一个非常可疑的事情。

简而言之,如果您愿意,只需打开/dev/fd文件(或{{1}})就可以逃脱,但这不是我推荐的内容。如果您正在使用的可执行文件没有按照您想要的方式调用库,我建议您直接调用库,编写自己的Python C扩展包装器或使用已经可用的Python API。