LINUX.ORG.RU

Использование модуля Subprocess в Python

 ,


0

1

Немного нубский вопрос, и всё же.
Такая задача: нужно обмениваться с программой строчками текста. Например как-то так

from subprocess import Popen, PIPE
proc = Popen(['cat', '-'], stdin=PIPE, stdout=PIPE)
for number in ('one', 'two', 'three'):
   proc.stdin.write(number + '\n')
   print proc.stdout.read()

В консоли вызвав «cat -» получается ответ при нажатии Enter.
При чтении stdout в Python, вызов блокируется. В этом и проблема.

Работать будет, если закрыть stdin или использовать subprocess.communicate. Но communicate тоже закрывает stdin, а он должен оставаться открытым. Посылка EOF (вроде 0x1a) после строки тоже не действует.

Как можно решить эту проблему?



Последнее исправление: ntohins (всего исправлений: 1)

Понял, что subprocess для таких вещей использовать совсем нельзя. Буду копаться в сторону tty.

A pipe works fine for getting the output to non-interactive programs. If you just want to get the output from ls, uname, or ping then this works. Pipes do not work very well for interactive programs and pipes will almost certainly fail for most applications that ask for passwords such as telnet, ftp, or ssh.

There are two reasons for this. First an application may bypass stdout and print directly to its controlling TTY. Something like SSH will do this when it asks you for a password. This is why you cannot redirect the password prompt because it does not go through stdout or stderr.

The second reason is because most applications are built using the C Standard IO Library (anything that uses #include <stdio.h>). One of the features of the stdio library is that it buffers all input and output. Normally output is line buffered when a program is printing to a TTY (your terminal screen). Everytime the program prints a line-feed the currently buffered data will get printed to your screen. The problem comes when you connect a pipe. The stdio library is smart and can tell that it is printing to a pipe instead of a TTY. In that case it switches from line buffer mode to block buffered. In this mode the currently buffered data is flushed when the buffer is full. This causes most interactive programs to deadlock. Block buffering is more efficient when writing to disks and pipes. Take the situation where a program prints a message «Enter your user name:\n» and then waits for you type type something. In block buffered mode, the stdio library will not put the message into the pipe even though a linefeed is printed. The result is that you never receive the message, yet the child application will sit and wait for you to type a response. Don't confuse the stdio lib's buffer with the pipe's buffer. The pipe buffer is another area that can cause problems. You could flush the input side of a pipe, whereas you have no control over the stdio library buffer.

More information: the Standard IO library has three states for a FILE *. These are: _IOFBF for block buffered; _IOLBF for line buffered; and _IONBF for unbuffered. The STDIO lib will use block buffering when talking to a block file descriptor such as a pipe. This is usually not helpful for interactive programs. Short of recompiling your program to include fflush() everywhere or recompiling a custom stdio library there is not much a controlling application can do about this if talking over a pipe.

The program may have put data in its output that remains unflushed because the output buffer is not full; then the program will go and deadlock while waiting for input — because you never send it any because you are still waiting for its output (still stuck in the STDIO's output buffer).

The answer is to use a pseudo-tty. A TTY device will force line buffering (as opposed to block buffering). Line buffering means that you will get each line when the child program sends a line feed. This corresponds to the way most interactive programs operate — send a line of output then wait for a line of input.

ntohins
() автор топика
Ответ на: комментарий от tailgunner

Нет. Python вызывает процесс, и создаёт два пайпа, при помощи которых может общаться с процессом. stdout Python будет stdin у вызываемого процесса и наоборот.

ntohins
() автор топика
Ответ на: комментарий от ntohins

Тогда просто используй неблокирующийся ввод/вывод в Python. Pseudo-tty - вещь специфическая, предназначенная для запуска диалоговых терминальных программ «под контролем». Я, конечно, не знаю, какую задачу ты решаешь, но всё же сомневаюсь, что тебе нужны именно pseudo-tty.

tailgunner ★★★★★
()
Ответ на: комментарий от ntohins

посмотри os.forkpty(). Я на нём самодельный «ssh» соорудил :)

true_admin ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.