用于 Windows 的 python os.mkfifo()

发布于 2024-09-03 01:25:12 字数 1752 浏览 9 评论 0原文

简短版本(如果你能回答简短版本,它可以为我完成工作,其余的主要是为了其他具有类似任务的人的利益):

在 Windows 中的 python 中,我想创建 2 个文件对象,附加到同一个文件对象文件(不一定是硬盘驱动器上的实际文件),一个用于读取,一个用于写入,这样如果读取端尝试读取它永远不会得到 EOF(它只会阻塞,直到写入内容)。我认为在 Linux 中 os.mkfifo() 可以完成这项工作,但在 Windows 中它不存在。可以做什么? (我必须使用文件对象)。

一些额外的细节: 我有一个 python 模块(不是我写的),它通过 stdin 和 stdout (使用 raw_input() 和 print)玩某个游戏。我还有一个 Windows 可执行文件,通过 stdin 和 stdout 玩同一游戏。我想让他们一对一地比赛,并记录他们所有的交流。

这是我可以编写的代码(get_fifo() 函数未实现,因为我不知道如何在 Windows 上执行此操作):

class Pusher(Thread):
        def __init__(self, source, dest, p1, name):
                Thread.__init__(self)
                self.source = source
                self.dest = dest
                self.name = name
                self.p1 = p1

        def run(self):
                while (self.p1.poll()==None) and\
                      (not self.source.closed) and (not self.source.closed):
                        line = self.source.readline()
                        logging.info('%s: %s' % (self.name, line[:-1]))
                        self.dest.write(line)
                        self.dest.flush()


exe_to_pythonmodule_reader, exe_to_pythonmodule_writer =\
                          get_fifo()
pythonmodule_to_exe_reader, pythonmodule_to_exe_writer =\
                          get_fifo()

p1 = subprocess.Popen(exe, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE)

old_stdin = sys.stdin
old_stdout = sys.stdout

sys.stdin = exe_to_pythonmodule_reader
sys.stdout = pythonmodule_to_exe_writer

push1 = Pusher(p1.stdout, exe_to_pythonmodule_writer, p1, '1')
push2 = Pusher(pythonmodule_to_exe_reader, p1.stdin, p1, '2')

push1.start()
push2.start()
ret = pythonmodule.play()
sys.stdin = old_stdin
sys.stdout = old_stdout

Short version (if you can answer the short version it does the job for me, the rest is mainly for the benefit of other people with a similar task):

In python in Windows, I want to create 2 file objects, attached to the same file (it doesn't have to be an actual file on the hard-drive), one for reading and one for writing, such that if the reading end tries to read it will never get EOF (it will just block until something is written). I think in linux os.mkfifo() would do the job, but in Windows it doesn't exist. What can be done? (I must use file-objects).

Some extra details:
I have a python module (not written by me) that plays a certain game through stdin and stdout (using raw_input() and print). I also have a Windows executable playing the same game, through stdin and stdout as well. I want to make them play one against the other, and log all their communication.

Here's the code I can write (the get_fifo() function is not implemented, because that's what I don't know to do it Windows):

class Pusher(Thread):
        def __init__(self, source, dest, p1, name):
                Thread.__init__(self)
                self.source = source
                self.dest = dest
                self.name = name
                self.p1 = p1

        def run(self):
                while (self.p1.poll()==None) and\
                      (not self.source.closed) and (not self.source.closed):
                        line = self.source.readline()
                        logging.info('%s: %s' % (self.name, line[:-1]))
                        self.dest.write(line)
                        self.dest.flush()


exe_to_pythonmodule_reader, exe_to_pythonmodule_writer =\
                          get_fifo()
pythonmodule_to_exe_reader, pythonmodule_to_exe_writer =\
                          get_fifo()

p1 = subprocess.Popen(exe, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE)

old_stdin = sys.stdin
old_stdout = sys.stdout

sys.stdin = exe_to_pythonmodule_reader
sys.stdout = pythonmodule_to_exe_writer

push1 = Pusher(p1.stdout, exe_to_pythonmodule_writer, p1, '1')
push2 = Pusher(pythonmodule_to_exe_reader, p1.stdin, p1, '2')

push1.start()
push2.start()
ret = pythonmodule.play()
sys.stdin = old_stdin
sys.stdout = old_stdout

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

野生奥特曼 2024-09-10 01:25:12

看了上面两个答案,无意间碰到了答案。 os.pipe() 完成了这项工作。谢谢您的回答。

我发布完整的代码以防其他人正在寻找这个:

import subprocess
from threading import Thread
import time
import sys
import logging
import tempfile
import os

import game_playing_module

class Pusher(Thread):
    def __init__(self, source, dest, proc, name):
        Thread.__init__(self)
        self.source = source
        self.dest = dest
        self.name = name
        self.proc = proc

    def run(self):
        while (self.proc.poll()==None) and\
              (not self.source.closed) and (not self.dest.closed):
            line = self.source.readline()
            logging.info('%s: %s' % (self.name, line[:-1]))
            self.dest.write(line)
            self.dest.flush()

def get_reader_writer():
    fd_read, fd_write = os.pipe()
    return os.fdopen(fd_read, 'r'), os.fdopen(fd_write, 'w')

def connect(exe):
    logging.basicConfig(level=logging.DEBUG,\
                        format='%(message)s',\
                        filename=LOG_FILE_NAME,
                        filemode='w')

    program_to_grader_reader, program_to_grader_writer =\
                              get_reader_writer()

    grader_to_program_reader, grader_to_program_writer =\
                              get_reader_writer()

    p1 = subprocess.Popen(exe, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE)        

    old_stdin = sys.stdin
    old_stdout = sys.stdout

    sys.stdin = program_to_grader_reader
    sys.stdout = grader_to_program_writer

    push1 = Pusher(p1.stdout, program_to_grader_writer, p1, '1')
    push2 = Pusher(grader_to_program_reader, p1.stdin, p1, '2')

    push1.start()
    push2.start()

    game_playing_module.play()

    sys.stdin = old_stdin
    sys.stdout = old_stdout

    fil = file(LOG_FILE, 'r')
    data = fil.read()
    fil.close()
    return data

if __name__=='__main__':
    if len(sys.argv) != 2:
        print 'Usage: connect.py exe'
        print sys.argv
        exit()
    print sys.argv
    print connect(sys.argv[1])

Following the two answers above, I accidentally bumped into the answer. os.pipe() does the job. Thank you for your answers.

I'm posting the complete code in case someone else is looking for this:

import subprocess
from threading import Thread
import time
import sys
import logging
import tempfile
import os

import game_playing_module

class Pusher(Thread):
    def __init__(self, source, dest, proc, name):
        Thread.__init__(self)
        self.source = source
        self.dest = dest
        self.name = name
        self.proc = proc

    def run(self):
        while (self.proc.poll()==None) and\
              (not self.source.closed) and (not self.dest.closed):
            line = self.source.readline()
            logging.info('%s: %s' % (self.name, line[:-1]))
            self.dest.write(line)
            self.dest.flush()

def get_reader_writer():
    fd_read, fd_write = os.pipe()
    return os.fdopen(fd_read, 'r'), os.fdopen(fd_write, 'w')

def connect(exe):
    logging.basicConfig(level=logging.DEBUG,\
                        format='%(message)s',\
                        filename=LOG_FILE_NAME,
                        filemode='w')

    program_to_grader_reader, program_to_grader_writer =\
                              get_reader_writer()

    grader_to_program_reader, grader_to_program_writer =\
                              get_reader_writer()

    p1 = subprocess.Popen(exe, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE)        

    old_stdin = sys.stdin
    old_stdout = sys.stdout

    sys.stdin = program_to_grader_reader
    sys.stdout = grader_to_program_writer

    push1 = Pusher(p1.stdout, program_to_grader_writer, p1, '1')
    push2 = Pusher(grader_to_program_reader, p1.stdin, p1, '2')

    push1.start()
    push2.start()

    game_playing_module.play()

    sys.stdin = old_stdin
    sys.stdout = old_stdout

    fil = file(LOG_FILE, 'r')
    data = fil.read()
    fil.close()
    return data

if __name__=='__main__':
    if len(sys.argv) != 2:
        print 'Usage: connect.py exe'
        print sys.argv
        exit()
    print sys.argv
    print connect(sys.argv[1])
知足的幸福 2024-09-10 01:25:12

对于跨平台解决方案,我建议在本地主机(127.0.0.1)上的套接字之上构建类似文件的对象——这就是IDLE默认所做的,以解决与您的问题非常相似的问题。

For a cross-platform solution, I'd recommend building the file-like object on top of a socket on localhost (127.0.0.1) -- that's what IDLE does by default to solve a problem that's quite similar to yours.

又爬满兰若 2024-09-10 01:25:12

在 Windows 上,您正在查看 (命名或匿名)管道

管道是进程用于通信的共享内存的一部分。创建管道的进程是管道服务器。连接到管道的进程是管道客户端。一个进程将信息写入管道,然后另一个进程从管道读取信息。

要使用 Windows Pipes,您可以使用 Python for Windows 扩展 (pywin32),或 Ctypes 模块。一个特殊的实用程序模块 win32pipe 提供了 win32 管道的接口蜜蜂。它包括 popen[234]() 便利函数的实现。

请参阅如何使用-win32-apis-with-python和类似的SO问题(不是特定于管道,但指向有用的信息)。

On Windows, you are looking at (Named or Anonymous) Pipes.

A pipe is a section of shared memory that processes use for communication. The process that creates a pipe is the pipe server. A process that connects to a pipe is a pipe client. One process writes information to the pipe, then the other process reads the information from the pipe.

To work with Windows Pipes, you can use Python for Windows extensions (pywin32), or the Ctypes module. A special utility module, win32pipe, provides an interface to the win32 pipe API's. It includes implementations of the popen[234]() convenience functions.

See how-to-use-win32-apis-with-python and similar SO questions (not specific to Pipes, but points to useful info).

夏日落 2024-09-10 01:25:12

os.pipe() 返回一个匿名管道,它非常轻量且高效。 (在 Windows 上,“匿名管道是使用具有唯一名称的命名管道。”)

TCP 套接字(如 user1495323 建议)更加重量级:例如,您可以使用 netstat 查看它们,每个端口都需要一个端口号,并且每个对等点的可用端口数限制为 64k(例如从 localhost 到 localhost 的 64k)。

另一方面,命名管道(在 Windows 上)受到限制,因为:

并且可以使用 makefile() 将套接字包装在与 Python 兼容的文件句柄中,这样就可以使用它们重定向 stdout 或 stderr。这使得这对于某些用例来说是一种有吸引力的选择,例如从一个线程发送 stdout 到另一个线程。

可以使用自动分配的端口号来构造套接字,如下所示(基于 优秀的Python套接字HOWTO):

with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as input_socket:
    # Avoid socket exhaustion by setting SO_REUSEADDR <https://stackoverflow.com/a/12362623/648162>:
    input_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    # localhost doesn't work if the definition is missing from the hosts file,
    # and 127.0.0.1 only works with IPv4 loopback, but socket.gethostname()
    # should always work:
    input_socket.bind((socket.gethostname(), 0))
    random_port_number = input_socket.getsockname()[1]
    input_socket.listen(1)

    # Do something with input_socket, for example pass it to another thread.

    output_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # close() should not strictly be necessary here, but since connect() could fail, it avoids leaking fds
    # in that case. "If a file descriptor is given, it is closed when the returned I/O object is closed".
    with output_socket:
        output_socket.connect((socket.gethostname(), random_port_number))

input_socket的用户(例如另一个线程)可以执行以下操作:

with input_socket:
    while True:
        readables, _, _ = select.select([input_socket], [], [input_socket], 1.0)

        if len(readables) > 0:
            input_conn, addr = self.input_socket.accept()
            break

    with input_conn:
        while True:
            readables, _, errored = select.select([input_conn], [], [input_conn], 1.0)
            if len(errored) > 0:
                print("connection errored, stopping")
                break

            if len(readables) > 0:
                read_data = input_conn.recv(1024)
                if len(read_data) == 0:
                    print("connection closed, stopping")
                    break
                else:
                    print(f"read data: {read_data!r}")

os.pipe() returns an anonymous pipe, which is very lightweight and efficient. (On Windows, "Anonymous pipes are implemented using a named pipe with a unique name.")

TCP sockets (as suggested by user1495323) are more heavyweight: you can see them with netstat for example, and each one requires a port number, and the number of available ports is limited to 64k per peer (e.g. 64k from localhost to localhost).

On the other hand, named pipes (on Windows) are limited because:

And sockets can be wrapped in Python-compatible filehandles using makefile(), which allows them to be used to redirect stdout or stderr. This makes this an attractive option for some use cases, such as sending stdout from one thread to another.

A socket can be constructed with an automatically-assigned port number like this (based on the excellent Python socket HOWTO):

with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as input_socket:
    # Avoid socket exhaustion by setting SO_REUSEADDR <https://stackoverflow.com/a/12362623/648162>:
    input_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    # localhost doesn't work if the definition is missing from the hosts file,
    # and 127.0.0.1 only works with IPv4 loopback, but socket.gethostname()
    # should always work:
    input_socket.bind((socket.gethostname(), 0))
    random_port_number = input_socket.getsockname()[1]
    input_socket.listen(1)

    # Do something with input_socket, for example pass it to another thread.

    output_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # close() should not strictly be necessary here, but since connect() could fail, it avoids leaking fds
    # in that case. "If a file descriptor is given, it is closed when the returned I/O object is closed".
    with output_socket:
        output_socket.connect((socket.gethostname(), random_port_number))

The user of input_socket (e.g. another thread) can then do:

with input_socket:
    while True:
        readables, _, _ = select.select([input_socket], [], [input_socket], 1.0)

        if len(readables) > 0:
            input_conn, addr = self.input_socket.accept()
            break

    with input_conn:
        while True:
            readables, _, errored = select.select([input_conn], [], [input_conn], 1.0)
            if len(errored) > 0:
                print("connection errored, stopping")
                break

            if len(readables) > 0:
                read_data = input_conn.recv(1024)
                if len(read_data) == 0:
                    print("connection closed, stopping")
                    break
                else:
                    print(f"read data: {read_data!r}")
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文