Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python - Reading Powershell script output, using subprocess, want to receive stdout line by line

Been Trying to read stdout from a Powershell script that runs for a little bit, generating output based on the number of computers it's pinging.

Trying to get the data to stream into the text box, but after all I've tried, I only seem to be able to get it to give all the output at once.

Been trying not to use subprocess.communicate(), as it also seems to give all the output at once.

Here is the Code:

from tkinter import *
import os
from subprocess import Popen, PIPE


window = Tk()
window.title( 'PowerShell Script Temp' )

frame = Frame(window)

fldrPath = r"C:/Users/firstname.lastname/Downloads/Powershell Development/Monthly Scans/"

listbox = Listbox(frame)
listbox.configure(width=50)

for name in os.listdir(fldrPath):
    listbox.insert('end', name)

def selection():
    fileList = listbox.curselection()
    for file in fileList:
        os.chdir(fldrPath)
        
        # Right here is the problematic section
        with Popen(["powershell.exe", '-File', fldrPath + '\\' + listbox.get(file)], stdout=PIPE, bufsize=1,
                   universal_newlines=True) as p:
            for line in p.stdout:
                output.insert('end', line)
                print(line, end='')   

output = Text(window, width=75, height=6, wrap=WORD, background="white")

btn = Button(frame, text='Run Script', command=selection)

btn.pack(side=RIGHT, padx=5)
listbox.pack(side=LEFT)
frame.pack(padx=30, pady=30)
output.pack(fill=BOTH, expand=1, padx=5, pady=5)

window.mainloop()

Doesn't filter line-by-line - just vomits it all after the PS script has finished.

I've tried quite a few other things:

Attempt 1

proc = subprocess.Popen(["powershell.exe", '-File', fldrPath + '\\' + listbox.get(file)], shell=True, stdout=PIPE)
    while proc.poll() is None:
         data = proc.stdout.readline()  # Alternatively proc.stdout.read(1024)
         print(data)
         text_box.insert('end', data)

Attempt 2

outpipe = subprocess.Popen(["powershell.exe", '-File', fldrPath + '\\' + listbox.get(file)],
                                stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE).communicate()[0]
    text_box.insert("end", outpipe)

Attempt 3

with Popen(["powershell.exe", '-File', fldrPath + '\\' + listbox.get(file)], stdout=PIPE, bufsize=1,
         universal_newlines=True) as p, StringIO() as buf:
         for line in p.stdout:
             print(line, end='')
             buf.write(line)
         outpipe = buf.getvalue()
         output.insert('end', outpipe)

Attempt 4

proc = subprocess.Popen(["powershell.exe", '-File', fldrPath + '\\' + listbox.get(file)], shell=True,
                             stdout=subprocess.PIPE)
     while proc.poll() is None:
         p = proc.stdout.readline()
         output.insert('end', p)

I've actually attempted far more, but can't seem to remember them. Lots of fors and withs...I'm a bit tired of it at this point 😔. I can get this thing to Print() in a manner that I desire, but not stream into the text box; almost like .insert() is too slow.

If someone could lead me in the right direction, I'd greatly appreciate it. Tried almost every similar thread on here that I could manage, just doesn't seem to be doing the trick.

like image 513
Paul Sisson Avatar asked Nov 06 '22 05:11

Paul Sisson


1 Answers

Trying to get the data to stream into the text box, but after all I've tried, I only seem to be able to get it to give all the output at once.

This code here:

def selection():
    fileList = listbox.curselection()
    for file in fileList:
        os.chdir(fldrPath)
        
        # Right here is the problematic section
        with Popen(["powershell.exe", '-File', fldrPath + '\\' + listbox.get(file)], stdout=PIPE, bufsize=1,
                   universal_newlines=True) as p:
            for line in p.stdout:
                output.insert('end', line)
                print(line, end='') 

is executed and after its finished you can see what it has done. To update it you could use the after method of tkinter and check for changes in the given time. Like in this exampel here.

The tkinter mainloop works like this

     Start
       |
       |<----------------------------------------------------------+
       v                                                           ^
   Do I have    No[*]  Calculate how            Sleep for at       |
   work to do?  -----> long I may sleep  -----> most that much --->|
       |                                        time               |
       | Yes                                                       |
       |                                                           |
       v                                                           |
   Do one callback                                                 |
       |                                                           |
       +-----------------------------------------------------------+

Another suggestion would be to use another thread. Also you should read that answer here for .communicate().

like image 148
Thingamabobs Avatar answered Nov 14 '22 21:11

Thingamabobs