如何正确使用Python中的subprocess.Popen线程?

huangapple go评论78阅读模式
英文:

How to properly use subprocess.Popen thread's in python?

问题

self.flag = IntVar()

# process for bash 1
self.process1 = subprocess.Popen(["bash"], stderr=subprocess.PIPE, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
# process for bash 2
self.process2 = subprocess.Popen(["bash"], stderr=subprocess.PIPE, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
exit = False

# start threads (stdout and stderror monitor)
threading.Thread(target=self.read_stdout_1).start()
threading.Thread(target=self.read_stderror_1).start()
threading.Thread(target=self.read_stdout_2).start()
threading.Thread(target=self.read_stderror_2).start()

self.frame2 = tk.Frame(self.ws, bg="#777474")
self.frame2.grid(column=0, row=0, padx=(100, 100), pady=(10, 10), sticky="W")

# Start button
self.btnVerify = Button(self.ws, text='Start', command=self.ButtonClick)
self.btnVerify.grid(row=5, columnspan=3, pady=10)

# output text area
self.logArea = tkst.ScrolledText(self.ws, wrap=tk.WORD, width=80, height=20, name="logArea")
self.logArea.grid(padx=(100, 100), pady=(10, 10), row=10, sticky="W")


def read_stdout_1(self):
    while not exit:
        msg = self.process1.stdout.readline()
        self.logArea.insert(INSERT, "process_1 logging start")
        self.logArea.insert(INSERT, msg.decode())
        self.logArea.insert(END, "\n logging end")


def read_stderror_1(self):
    while not exit:
        msg = self.process1.stderr.readline()
        self.logArea.insert(INSERT, "Error")
        self.logArea.insert(INSERT, msg.decode())
        self.logArea.insert(END, "\n Error end")


def read_stdout_2(self):
    while not exit:
        msg = self.process2.stdout.readline()
        self.logArea.insert(INSERT, "process_2 logging start")
        self.logArea.insert(INSERT, msg.decode())
        self.logArea.insert(END, "\nlogging end")


def read_stderror_2(self):
    while not exit:
        msg = self.process2.stderr.readline()
        self.logArea.insert(INSERT, "Error")
        self.logArea.insert(INSERT, msg.decode())
        self.logArea.insert(END, "\n Error end")


def ButtonClick(self):
    print("Button clicked")

    # set flag to 1
    self.flag = 1

    while not exit:
        if self.flag == 1:
            print("Case 1")
            self.process1.stdin.write(r'python C:\path\test1.py'.encode())
            self.process1.stdin.flush()
            self.flag = 2

        elif self.flag == 2:
            print("Case 2")
            self.process1.stdin.write(r'python C:\path\test2.py'.encode())
            self.process1.stdin.flush()
            self.flag = 3

        elif self.flag == 3:
            print("Case 3")
            self.process2.stdin.write(r'python C:\path\test3.py'.encode())
            self.process2.stdin.flush()
            self.flag = 4

        elif self.flag == 4:
            print("Case 3")
            self.process2.stdin.write(r'python C:\path\test4.py'.encode())
            self.process2.stdin.flush()
            self.flag = 5

        else:
            break


def run(self):
    self.ws.title('Test Script')
    self.ws.mainloop()

validator = TKValidator()
validator.run()
英文:

I am writing a python script which will execute some another script from bash terminals. I need two bash terminal and execute some script in that bash terminal one by one. After executing the script, I will read the out put messages to a Tkinter text area. I have designed simple python script which will create 2 process. I am using thread to monitor the stdout and stderror for each processes. I am using while loop to hold the process.

Here is the my script

import os
import tkinter as tk
from tkinter import *
import tkinter.scrolledtext as tkst
import subprocess
from subprocess import Popen
import threading
class TKValidator:
def __init__(self): 
#create TK
self.ws = Tk()
self.ws.state('zoomed')
self.flag = IntVar()
#process for bash 1
self.process1 = subprocess.Popen(["bash"], stderr=subprocess.PIPE,shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
#process for bash 2
self.process2 = subprocess.Popen(["bash"], stderr=subprocess.PIPE,shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
exit = False
#start threads (stdout and stderror monitor)
threading.Thread(target=self.read_stdout_1).start()
threading.Thread(target=self.read_stderror_1).start()
threading.Thread(target=self.read_stdout_2).start()
threading.Thread(target=self.read_stderror_2).start()
self.frame2 = tk.Frame(self.ws, bg="#777474")
self.frame2.grid(column=0, row=0, padx=(100,100), pady=(10, 10), sticky="W")
#Start button
self.btnVerify = Button(self.ws, text='Start', command=self.ButtonClick)
self.btnVerify.grid(row=5, columnspan=3, pady=10)
#output text area
self.logArea = tkst.ScrolledText(self.ws, wrap= tk.WORD, width=80, height=20, name="logArea")
self.logArea.grid(padx=(100,100), pady=(10, 10) ,row=10, sticky="W")
def read_stdout_1(self):
while not exit:
msg = self.process1.stdout.readline()
self.logArea.insert(INSERT, "process_1 logging start")
self.logArea.insert(INSERT, msg.decode())
self.logArea.insert(END, "\n logging end")
def read_stderror_1(self):
while not exit:
msg = self.process1.stderr.readline()
self.logArea.insert(INSERT, "Error")
self.logArea.insert(INSERT, msg.decode())
self.logArea.insert(END, "\n Error end")
def read_stdout_2(self):
while not exit:
msg = self.process2.stdout.readline()
self.logArea.insert(INSERT, "process_2 logging start")
self.logArea.insert(INSERT, msg.decode())
self.logArea.insert(END, "\logging end")
def read_stderror_2(self):
while not exit:
msg = self.process2.stderr.readline()
self.logArea.insert(INSERT, "Error")
self.logArea.insert(INSERT, msg.decode())
self.logArea.insert(END, "\n Error end")
def ButtonClick(self):
print("Button clicked")
#set flag to 1
self.flag = 1
while not exit:
if self.flag == 1:
print("Case 1")
self.process1.stdin.write(r'python C:\path\test1.py'.encode())
self.process1.stdin.flush()
self.flag = 2
elif self.flag == 2:
print("Case 2")
self.process1.stdin.write(r'python C:\path\test2.py'.encode())
self.process1.stdin.flush()
self.flag = 3
elif self.flag == 3:
print("Case 3")
self.process2.stdin.write(r'python C:\path\test3.py'.encode())
self.process2.stdin.flush()
self.flag = 4
elif self.flag == 4:
print("Case 3")
self.process2.stdin.write(r'python C:\path\test4.py'.encode())
self.process2.stdin.flush()
self.flag = 5
else:
break
def run(self):
self.ws.title('Test Script')
self.ws.mainloop() 
validator = TKValidator()
validator.run() 

But after exectuing this script, I can see only the output like

Button clicked

I want to run the while loop one by one but it is not working. Can you please suggest, what is the problem here and how can I fix this to work as I expected ?

答案1

得分: 0

这里的简短回答是这行代码:

while not exit:

这里的exit值始终为True。尝试打印exit以查看其实际值:

print(exit, bool(exit))
while not exit:

顺便提一下,由于这个原因,exit是一个不好的变量名。

更详细的回答是:你为什么要以这种方式进行操作?据我所知(我承认我只是粗略浏览了代码,控制流相当复杂,我不确定我是否理解了它),你的要求是:

  • 当单击按钮时,应启动两个脚本
  • 当它们完成(或在运行时?)时,它们的输出应打印到文本区域

没有必要通过启动shell、向其stdin提供输入、轮询其stdout等来进行所有这些操作。只需启动一个进程,等待它并将输出放入文本区域,或者在运行时轮询并将输出放入文本区域。如果你想同时从两个子进程输出,你需要解决冲突的逻辑,可能需要使用某种队列。

如果你需要与脚本进行交互,我强烈建议你使用pexpect

英文:

The short answer here is this line:

while not exit:

The value of exit here is always True. Try printing exit to find out what it actually is:

print(exit, bool(exit))
while not exit:

Incidentally exit was a bad variable name for this reason.

The longer answer is: why are you doing things this way? As far as I can tell (I admit I only skimmed the code and the control flow manages to be sufficiently convoluted that I'm not sure I followed it) your requirements are:

  • when a button is clicked, two scripts should be launched
  • when they are finished (or whilst running?), their outputs should be printed to the textarea

There is no reason to mess about with starting shells, feeding to their stdin, polling their stdout, etc, for any of this. Just launch a process, join it and drop the output into the textarea or poll it whist running and output to the textarea. If you want to output from two subprocesses at once you need deconfliction logic: probably a queue of some description.

If you need to interact with the scripts I strongly recommend you use pexpect.

huangapple
  • 本文由 发表于 2023年5月24日 22:09:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/76324479.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定