I had to quickly learn Python. I can’t solve a simple (I think) task: on a Linux system, start a child process of another program and redirect it to stdin, stdout to its own, respectively, stdout and stdin. It is fundamentally important that the program live and not die after a single write-read transaction. In short, you need to set up a long-term command-response interaction. You cannot also use a shell (that is, setting up channels through the shell), only programmatically.

I write something like this:

import subprocess proc = subprocess.Popen(['myprog', '-l'], shell = True, stdin = subprocess.PIPE, stdout = subprocess.PIPE) #допустим, я сохранил proc и через какое-то время пытаюсь с ним взаимодействовать: if proc.returncode == None: proc.stdin.write(b'command') response = process.communicate()[0] 

If you run this code once, it will return an empty string. If looped, raises BrokenPipeError. This behavior is incomprehensible to me.

Why such code does not work? communicate () waiting for the process to complete? But if this is so, then why is the verification if proc.returncode == None valid? The process is alive. What can I do to make this work (tell me at least what to use)?

  • one
    aside: 1- do not use shell=True if you send the command as a list (this is an error in most cases). You probably want to send the line containing the command to either remove shell=True 2 - why you expect that process.returncode is not None BEFORE you called process.communicate() (AFTER the process returns this function is dead) 3- process.communicate(input=b'command') passes the command, closes the stdin stream, reads the output and waits until the child process terminates, returning its output. - jfs

1 answer 1

In order to interactively communicate with the child process, it is convenient to use the pexpect module . You can directly call the subprocess , but this requires taking into account manually a few moments that pexpect already implements — the subprocess in this sense is too low for the beginner (the greatest strength, but less convenience).

Example from documentation :

 # This connects to the openbsd ftp site and # downloads the recursive directory listing. import pexpect child = pexpect.spawn('ftp ftp.openbsd.org') child.expect('Name .*: ') child.sendline('anonymous') child.expect('Password:') child.sendline('noah@example.com') child.expect('ftp> ') child.sendline('lcd /tmp') child.expect('ftp> ') child.sendline('cd pub/OpenBSD') child.expect('ftp> ') child.sendline('get README') child.expect('ftp> ') child.sendline('bye') 

If you are forced to use pexpect it says that you should look for an API for the desired functionality. For example, instead of calling the command line utility, you should call functions from the C library directly (if the library exists). In the ftp example, you can use the ftplib module from the standard library in Python, instead of creating a child ftp process.