Как открыть специальный файл устройства символов именованного канала для чтения и записи на Python
у меня есть служба, работающая на Linux box, которая создает именованный канал символьное устройство-специальный файл, и я хочу написать программу Python3, которая взаимодействует с сервисом, записывая текстовые команды и читая текстовые ответы из труба устройства. У меня нет исходного кода для службы.
я могу использовать os.open(named_pipe_pathname, os.O_RDWR)
, и я могу использовать os.read(...)
и os.write(...)
читать и писать, но это боль, потому что я должен написать свой собственный код для преобразования между байтами и строками я должен написать свой собственный readline(...)
функция, etc.
Я бы предпочел использовать Python3 io
объект для чтения и записи труба устройства, но каждый способ я могу думать, чтобы создать один возвращает ту же ошибку:
io.UnsupportedOperation: File or stream is not seekable.
например, я получаю это сообщение, если я попробую open(pathname, "r+")
, и я получаю то же самое сообщение, если я попытаюсь fd=os.open(...)
следовал по os.fdopen(fd, "r+", ...)
.
Q: каков предпочтительный способ для программы Python3 писать и читать текст в именованный канал символьное устройство?
Edit:
Упс! Я предположил, что имею дело с именованным каналом, потому что документация для службы описывает его как "канал" и, поскольку он не появляется в файловой системе до запуска службы пользовательского режима. Но, Linux file
утилита говорит, что это на самом деле, персонаж специальный файл устройства.
3 ответов
проблема возникает из-за попытки использовать io.open
в режиме чтения-записи неявно пытается обернуть исходный файл в io.BufferedRandom
(который затем заворачивают в io.TextIOWrapper
Если в текстовом режиме), который предполагает, что базовый файл не только чтение/запись, но и произвольный доступ, и он берет на себя вольности (поиск неявно) на основе этого. Существует отдельный класс io.BufferedRWPair
, предназначенный для использования с трубами чтения/записи (docstring специально упоминает, что он используется для сокетов и двухсторонних труб).
вы можете имитировать эффекты io.open
вручную оборачивая слой за слоем, чтобы получить тот же конечный результат. В частности, для обертки текстового режима вы должны сделать что-то вроде:
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)
Я считаю, что это будет близко rawf
два раза, когда txtf
закрыт, но, к счастью, двойное закрытие здесь безвредно (второе close
ничего не делает, понимая, что он уже закрыт).
решение
можно использовать pexpect
. Вот пример использования двух модулей python:
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
x = ''
while True:
x = input(x[::-1] + ' > ')
объяснение
caller.py
использует "псевдо-TTY устройство", чтобы поговорить с backwards.py
. Мы предоставляем ввод с sendline
и захват ввода с expect
(и ).
похоже, вам нужно создать отдельные дескрипторы для чтения и записи: для открытия чтения/записи просто требуется метод поиска. Я не мог понять, как тайм-аут чтения, поэтому приятно добавить открыватель (см. docstring для io.open
), который открывает считыватель в неблокирующем режиме. Я создал простой Эхо-сервис на именованной трубе под названием /tmp/test_pipe
:
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()
''