I have a function that sends a statement to bash. It prints the output in real time to the console and also returns the value. It works flawlessly and looks like this:
def call_in_bash(statement):
process = subprocess.Popen(['bash','-c',statement],stdout=subprocess.PIPE, universal_newlines=True, bufsize=1)
buffer = io.StringIO()
selector = selectors.DefaultSelector()
selector.register(process.stdout, selectors.EVENT_READ)
line = ''
while process.poll() is None:
events = selector.select()
for key, mask in events:
line = key.fileobj.readline()
buffer.write(line)
sys.stdout.write(line)
selector.close()
output = buffer.getvalue()
buffer.close()
return output
However, I'd like to make this a class with a constructor and a destructor so subsequent calls can keep the context until I destroy the object. That is, I can call_in_bash("cd folder") and then call_in_bash("do something") in that folder. To do so, I've tried to do something like this:
def __init__(self):
self.process: Popen(['bash'], stdin=subprocess.PIPE, stdout=subprocess.PIPE ...)
def call_to_bash(self, statement):
self.process.stdin.write(statement + "\n")
self.process.stdin.flush()
# from here on same as before
It locks and never finishes no matter the command. I don't know what big of a difference it makes if I call popen(['bash'] and then put stuff in its stdin instead of calling bash -c statement. In my mind they're pretty much the same thing but I'm clearly missing something important here. I have read lots of other questions and they address either keeping the context, returning the value or printing in real time, but never all three things at once.
You can do something like this to control and interactive shell with
pexpect:Adapt it to your needs.