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.
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.
Related
I wrote a Python script to run a terminal command that belongs to a 3rd party program.
import subprocess
DETACHED_PROCESS = 0x00000008
command = 'my cmd command'
process = subprocess.Popen(
args=command,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
encoding="utf-8",
creationflags=DETACHED_PROCESS
)
code = process.wait()
print(process.stdout.readlines())
# Output: []
This script basically runs the command successfully. However, I'd like to print the output but process.stdout.readlines() prints an empty list.
I need to run the subprocess with creationflags due to 3rd party program's terminal command.
I've also tried creationflags=subprocess.CREATE_NEW_CONSOLE. It works but process takes too long because of 3rd party program's terminal command.
Is there a way to print the output of subprocess by using creationflags=0x00000008 ?
By the way, I can use subprocess.run etc to run the command also but I'm wondering if I can fix this.
Thank you for your time!
Edit:
I'm sorry I forgot to say I can get output if i write "dir" etc. as a command. However, I can't get any output when I write a command such as: command = '"program.exe" test'
I'm not sure that this works for your specific case, but I use subprocess.check_output when I need to capture subprocess output.
import subprocess
DETACHED_PROCESS = 0x00000008
command = 'command'
process = subprocess.check_output(
args=command,
shell=True,
stderr=subprocess.STDOUT,
encoding="utf-8",
creationflags=DETACHED_PROCESS
)
print(process)
This just returns a string of stdout.
I've written some code that uses the Python subprocess module to open a PowerShell window, then run a command in that same window. The PS window opens, then almost immediately closes. The code below will open a PS window and leave it open if I removed the second item from cmd.
import subprocess
cmd = ['powershell', 'ls']
prompt = subprocess.Popen(cmd, stdin=subprocess.PIPE)
Add -noexit argument as follows (-noprofile is not mandatory):
import subprocess
cmd = ['powershell', '-noprofile', '-noexit', '&', 'ls *.csv']
prompt = subprocess.call ( cmd )
Result:
Any reason to not use subprocess.call instead? I think it would do exactly what you want.
Its because you forgot to communicate with process
Simply add a line
output, error = prompt.communicate() # this is to start the process
print(output) # add stdout=subprocess.PIPE
print(error) # add stderr=subprocess.PIPE
PS: I can't help you with powershell because i don't know powershell
Is there any way to use Popen with interactive commands? I mean nslookup, ftp, powershell... I read the whole subprocess documentation several times but I can't find the way.
What I have (removing the parts of the project which aren't of interest here) is:
from subprocess import call, PIPE, Popen
command = raw_input('>>> ')
command = command.split(' ')
process = Popen(command, stdout=PIPE, stderr=PIPE, shell=True)
execution = process.stdout.read()
error = process.stderr.read()
output = execution + error
process.stderr.close()
process.stdout.close()
print(output)
Basically, when I try to print the output with a command like dir, the output is a string, so I can work with the .read() on it. But when I try to use nslookup for example, the output isn't a string, so it can't be read, and the script enters in a deadlock.
I know that I can invoke nslookup in non-interactive mode, but that's not the point. I want to remove all the chances of a deadlock, and make it works with every command you can run in a normal cmd.
The real way the project works is through sockets, so the raw_input is a s.recv() and the output is sending back the output, but I have simplified it to focus on the problem.
I have a executable that lets me talk to a temperature controller. When I double-click the exe (SCPI-CLI.exe) it will open up a command window with text "TC_CLI>". I can then type my commands and talk to my controller: eg: TC:COMM:OPEN:SER 8
When I use the subprocess.Popen like this
import subprocess
text = 'tc:comm:open:ser 8'
proc = subprocess.Popen(['C:\\Program Files (x86)\\TC_SCPI\\lib\\SCPI-CLI.exe'],
stdout=subprocess.PIPE,stdin=subprocess.PIPE)
proc.stdin.write(text)
proc.stdin.close()
result = proc.stdout.read()
print(result)
the SCPI-CLI.exe will open up, but will not show me the > prompt. What am I doing wrong here? It will hang at the proc.stdin.write(text).
I am a newbie to sub-process.
You can try to add "\n" to your string, that way you send the enter key.
Also please try to add pipe to stderr also and see if it display any error (or maybe it use stderr instead of stdout for displaing messages)
One more thing, is a good idea to wait for this program to exit before reading the results.
Try this:
import subprocess
import time
import os
text = b'tc:comm:open:ser 8\nexit\n'
proc = subprocess.Popen(['SCPI-CLI.exe'],stdout=subprocess.PIPE,stdin=subprocess.PIPE,stderr=subprocess.PIPE)
out,err = proc.communicate(text)
print(out.decode())
I can capture the output of a command line execution, for example
import subprocess
cmd = ['ipconfig']
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
output = proc.communicate()[0]
print output
In the above example it is easy as ipconfig, when run directly on the command line, will print the output on the same console window. However, there can be situations where the command opens a new console window to display all the output. An example of this is running VLC.exe from the command line. The following VLC command will open a new console window to display messages:
vlc.exe -I rc XYZ.avi
I want to know how can I capture the output displayed on this second console window in Python. The above example for ipconfig does not work in this case.
Regards
SS
I think this is really matter of VLC interface. There may be some option to vlc.exe so it doesn't start the console in new window or provides the output otherwise. In general there is nothing that stops me to run a process which runs another process and does not provide its output to caller of my process.
Note that, in your second command, you have multiple arguments (-I, rc, and the file name - XYZ.avi), in such cases, your cmd variable should be a list of - command you want to run , followed by all the arguments for that command:
Try this:
import subprocess
cmd=['vlc.exe', '-I', 'rc', 'XYZ.avi']
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
output = proc.communicate()[0]
print output
If you are not running the script from right directory, you might want to provide absolute paths for both vlc.exe and your XYZ.avi in cmd var