简体   繁体   English

如何在Python中打开<del>命名管道</ del>字符设备专用文件进行读写

[英]How to open <del>named pipe</del>character device special file for reading and writing in Python

I have a service running on a Linux box that creates a 我有一个在Linux机器上运行的服务,可以创建一个 named pipe 命名管道 character device-special file, and I want to write a Python3 program that communicates with the service by writing text commands and reading text replies from the 字符设备特殊文件,我想编写一个Python3程序,通过编写文本命令和阅读文本回复来与服务进行通信 pipe device. 设备。 I don't have source code for the service. 我没有该服务的源代码。

I can use os.open(named_pipe_pathname, os.O_RDWR) , and I can use os.read(...) and os.write(...) to read and write it, but that's a pain because I have to write my own code to convert between bytes and strings, I have to write my own readline(...) function, etc. 我可以使用os.open(named_pipe_pathname, os.O_RDWR) ,我可以使用os.read(...)os.write(...)来读取和写入它,但这很痛苦,因为我必须写我自己的代码在字节和字符串之间转换,我必须编写自己的readline(...)函数等。

I would much rather use a Python3 io object to read and write the 我宁愿使用Python3 io对象来读写 pipe device, but every way I can think to create one returns the same error: 设备,但我能想到创建一个的方式返回相同的错误:

io.UnsupportedOperation: File or stream is not seekable.

For example, I get that message if I try open(pathname, "r+") , and I get that same message if I try fd=os.open(...) followed by os.fdopen(fd, "r+", ...) . 例如,如果我尝试open(pathname, "r+") ,我会得到该消息,如果我尝试fd=os.open(...)后跟os.fdopen(fd, "r+", ...)我得到相同的消息fd=os.open(...) os.fdopen(fd, "r+", ...)

Q: What is the preferred way for a Python3 program to write and read text to and from a 问:Python3程序在文本中写入和读取文本的首选方法是什么? named pipe 命名管道 character device? 角色设备?


Edit: 编辑:

Oops! 哎呀! I assumed that I was dealing with a named pipe because documentation for the service describes it as a "pipe" and, because it doesn't appear in the file system until the user-mode service runs. 我假设我正在处理一个命名管道,因为该服务的文档将其描述为“管道”,因为它在用户模式服务运行之前不会出现在文件系统中。 But, the Linux file utility says it is in fact, a character device special file. 但是,Linux file实用程序说它实际上是一个字符设备特殊文件。

Solution

You can use pexpect . 你可以使用pexpect Here is an example using two python modules: 以下是使用两个python模块的示例:

caller.py caller.py

import pexpect

proc = pexpect.spawn('python3 backwards.py')
proc.expect(' > ')

while True:

    n = proc.sendline(input('Feed me - '))
    proc.expect(' > ')
    print(proc.before[n+1:].decode())

backwards.py backwards.py

x = ''

while True:
    x = input(x[::-1] + ' > ')

Explanation 说明

caller.py is using a "Pseudo-TTY device" to talk to backwards.py . caller.py正在使用“Pseudo-TTY设备”与backwards.py交谈。 We are providing input with sendline and capturing input with expect (and the before attribute). 我们使用expect (以及before属性)提供sendline输入和捕获输入。

The problem occurs because attempting to use io.open in read-write mode implicitly tries to wrap the underlying file in io.BufferedRandom (which is then wrapped in io.TextIOWrapper if in text mode), which assumes the underlying file is not only read/write, but random access, and it takes liberties (seeking implicitly) based on this. 出现该问题是因为试图使用io.open在读写模式隐含地尝试包裹在底层文件io.BufferedRandom (其然后包裹在io.TextIOWrapper如果在文本模式下),其中假定基础文件是不是只读/ write,但随机访问,它基于此需要自由(寻求隐式)。 There is a separate class, io.BufferedRWPair , intended for use with read/write pipes (the docstring specifically mentions it being used for sockets and two way pipes). 有一个单独的类io.BufferedRWPair ,用于读/写管道(docstring特别提到它用于套接字和双向管道)。

You can mimic the effects of io.open by manually wrapping layer by layer to produce the same end result. 您可以通过逐层手动包装来模仿io.open的效果,以产生相同的最终结果。 Specifically, for a text mode wrapper, you'd do something like: 具体来说,对于文本模式包装器,您可以执行以下操作:

rawf = io.FileIO(named_pipe_pathname, mode="rb+")
with io.TextIOWrapper(io.BufferedRWPair(rawf, rawf), encoding='utf-8', write_through=True) as txtf:
    del rawf   # Remove separate reference to rawf; txtf manages lifetime now
    # Example use that works (but is terrible form, since communicating with
    # oneself without threading, select module, etc., is highly likely to deadlock)
    # It works for this super-simple case; presumably you have some parallel real code
    txtf.write("abcé\n")
    txtf.flush()
    print(txtf.readline(), flush=True)

I believe this will close rawf twice when txtf is closed, but luckily, double-close is harmless here (the second close does nothing, realizing it's already closed). 我相信当txtf关闭时,这将关闭rawf两次,但幸运的是,双关闭在这里是无害的(第二次close什么也没做,意识到它已经关闭)。

It looks like you need to create separate handles for reading and for writing: to open read/write just requires a seek method. 看起来你需要为阅读和写入创建单独的句柄:打开读/写只需要一个搜索方法。 I couldn't figure out how to timeout reading, so it's nice to add an opener (see the docstring for io.open ) that opens the reader in non-blocking mode. 我无法弄清楚如何超时读取,所以最好添加一个开启器(参见io.open的docstring),以非阻塞模式打开阅读器。 I set up a simple echo service on a named pipe called /tmp/test_pipe : 我在名为/tmp/test_pipe的命名管道上设置了一个简单的echo服务:

In [1]: import io
In [2]: import os
In [3]: nonblockingOpener = lambda name, flags:os.open(name, flags|os.O_NONBLOCK)
In [4]: reader = io.open('/tmp/test_pipe', 'r', opener = nonblockingOpener)
In [5]: writer = io.open('/tmp/test_pipe', 'w')
In [6]: writer.write('Hi have a line\n')
In [7]: writer.flush()
In [8]: reader.readline()
Out[8]: 'You said: Hi have a line\n'
In [9]: reader.readline()
''

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM