epic.jordan
epic.jordan

Reputation: 43

Capture standard output for command passed to an embedded terminal

I am trying to capture the output of a command that I am running with subprocess.Popen and outputting to an xterm terminal that is embedded in a tkinter built application. I want the output of the command to go to the terminal and be captured so I can analyze the information.

In another case where I just run the command with subprocess.Popen and don't pass the output to the terminal. I can get the output by including stdout=subprocess.PIPE, stderr=subprocess.STDOUT

proc_output = subprocess.Popen('ls', stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
print(str(proc_output.stdout.read()))

I am guessing that the way I have it coded I can send the output to the terminal or to a variable, but not both.

For my code example, once the terminal opens I have to manually type tty in the terminal and then put the output in the tty entry field. After that I can run commands from the cmd_entry and send the output to the terminal .

This code is a slightly modified version of the answer from here.

from tkinter import *
import subprocess

root = Tk()
termf = Frame(root, height=360, width=625)
termf.pack(fill=BOTH, expand=YES)
wid = termf.winfo_id()

f=Frame(root)
Label(f,text="/dev/pts/").pack(side=LEFT)
tty_index = Entry(f, width=3)
tty_index.insert(0, "1")
tty_index.pack(side=LEFT)
Label(f,text="Command:").pack(side=LEFT)
cmd_entry = Entry(f)
cmd_entry.insert(0, "ls -l")
cmd_entry.pack(side=LEFT, fill=X, expand=1)

def send_entry_to_terminal(*args):
    tty="/dev/pts/%s" % tty_index.get()
    proc = subprocess.Popen(f'{cmd_entry.get()} <{tty} >{tty} 2> {tty}',
                            shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    print(str(proc.stdout.read())
    # cmd(f'{e.get()} <{tty} >{tty} 2> {tty}')

cmd_entry.bind("<Return>", send_entry_to_terminal)
b = Button(f,text="Send", command=send_entry_to_terminal)
b.pack(side=LEFT)
f.pack(fill=X, expand=1)

# cmd(f'xterm -into {wid} -geometry 100x27 -sb -e "tty; sh" &')
subprocess.Popen(f'xterm -into {wid} -geometry 100x27 -sb &', shell=True)

root.mainloop()

What I want to do is have the output of any command ran with subprocess to print to the terminal and be saved in a variable for later processing. Yet right now I can only get one or the other.

Upvotes: 3

Views: 247

Answers (1)

Davis Herring
Davis Herring

Reputation: 39818

Start by converting the shell redirections into native Popen capabilities:

with open(tty,'r+b',0) as f:
  proc=subprocess.Popen(cmd_entry.get(),shell=True,
                        stdin=f,stdout=f,stderr=f)
if proc.wait(): warn_user(…)

Then it should be fairly obvious how to retain a copy of the output:

with open(tty,'r+b',0) as f:
  proc=subprocess.Popen(cmd_entry.get(),shell=True,
                        stdin=f,stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
  out=[]
  for l in proc.stdout:
    f.write(l)
    out.append(l)
  out=b"".join(out)
  if proc.wait(): warn_user(…)

Note that the process will then have a pipe rather than a (pseudo)terminal for its output, which may change its behavior (e.g., ls prints in a single column, and git doesn’t use a pager). If you want to preserve the terminal behavior, you have to open your own pseudoterminal and relay output written to it (which may then include various control sequences) to the one created by xterm.

You might also want to make the process be the foreground process group of the (xterm) terminal so that it can receive keyboard signals; this involves tcsetpgrp and is somewhat complicated.

Upvotes: 1

Related Questions