简体   繁体   English

通过子进程通过 python 运行 TCL,但不提供任何输出

[英]running TCL through python by subprocess, but not giving any output

I am trying to run my tcl script through python subprocess as follow:我正在尝试通过 python 子进程运行我的 tcl 脚本,如下所示:

import subprocess
>>> subprocess.Popen(["tclsh", "tcltest.tcl"])
<subprocess.Popen object at 0x0000000001DD4DD8>
>>> subprocess.Popen(["tclsh", "tcltest.tcl"], shell=True )
<subprocess.Popen object at 0x0000000002B34550>

I don't know if it is working or not, since I don't see any anything from it, my tcl script also has some packages from my company that causes errors when I use Tkinter, Tk, and eval,我不知道它是否有效,因为我没有看到任何东西,我的 tcl 脚本也有一些来自我公司的包,当我使用 Tkinter、Tk 和 eval 时会导致错误,

import Tkinter
import socket

def TCLRun():
 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 s.connect(('127.0.0.1', 5006))
 root = Tkinter.Tk()
## root.tk.eval('package require Azimuth-Sdk')
 tcl_script ="""
##package require Company-Sdk
## set players [ace_azplayer_list_players]
set players 2
puts $players 
##  if { $players != "" } {         
##  foreach player $players {   
##      set cmd ace_azplayer_remove_player
##      if { [catch { [ $cmd $player ] } err] } {   
##          puts " $cmd $player - $err" 
##          sleep 1 
##          }           
##      } 
##  } """
 # call the Tkinter tcl interpreter
 root.tk.call('eval', tcl_script)
 root.mainloop()

gives me this error给我这个错误

import TCLCall
>>> TCLCall.TCLRun()

Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    TCLCall.TCLRun()
  File "C:\Users\XXX\Desktop\PKT\TCLCall.py", line 24, in TCLRun
    root.tk.call('eval', tcl_script)
TclError: can not find channel named "stdout"

that's why I switched to subprocess.这就是为什么我切换到子流程。 at least it doesn't give me error!至少它不会给我错误!

any idea how to run my tcl script with internal required package through python?!知道如何通过 python 使用内部所需包运行我的 tcl 脚本吗?!

Thanks谢谢

To get the output from using subprocess.Popen , you can try the following:要使用subprocess.Popen获取输出,您可以尝试以下操作:

import subprocess

p = subprocess.Popen(
    "tclsh tcltest.tcl",
    shell=True,
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
print stdout
print stderr

It's entirely possible that the script you're running with subprocess.Popen is also generating an error, but isn't displaying since you aren't explicitly looking for it.您使用subprocess.Popen运行的脚本完全有可能也会生成错误,但不会显示,因为您没有明确查找它。

Edit:编辑:

To prevent some information from being lost in the comments below:为了防止在下面的评论中丢失一些信息:

You probably have several potential errors here, or things you can try.您可能在这里有几个潜在的错误,或者您可以尝试的事情。

Either your tcl script by itself isn't able to import teapot properly, or some sort of interaction between the tcl script and the python script isn't working properly, or subprocess.Popen isn't correctly finding the teapot package from your path.您的 tcl 脚本本身无法正确导入teapot ,或者 tcl 脚本和 python 脚本之间的某种交互无法正常工作,或者subprocess.Popen无法从您的路径中正确找到teapot包。

I would try debugging your programs in that order.我会尝试按该顺序调试您的程序。 First confirm that your tcl script works without using python or subprocess.Popen and just directly run it from the command line (for example, C:\Users\blah tclsh tcltest.tcl )首先确认你的 tcl 脚本在不使用 python 或subprocess.Popen的情况下工作,直接从命令行运行它(例如, C:\Users\blah tclsh tcltest.tcl

Then, after you've made sure your script work, bring in Python.然后,在您确保您的脚本工作后,引入 Python。 From what I'm seeing, you don't have any problem with the python stuff, but with either your tcl script or some issue with your path.从我所看到的,你对 python 的东西没有任何问题,但是你的 tcl 脚本或者你的路径有一些问题。

The whole point of subprocess.Popen is redirection of standard channels, so you can handle output programmatically instead of seeing it on your own standard output. subprocess.Popen的重点是标准通道的重定向,因此您可以以编程方式处理输出,而不是在您自己的标准输出中看到它。 Did you try handling it?你试过处理它吗? How?如何?

Maybe you don't need redirection at all: then os.system("tclsh tcltest.tcl") should be enough.也许您根本不需要重定向:那么os.system("tclsh tcltest.tcl")就足够了。 Or maybe subprocess.Popen has other advantages for you -- then figure out how to disable redirection, or how to redirect child's stdout to your own stdout.或者subprocess.Popen可能对您有其他优势——然后弄清楚如何禁用重定向,或者如何将孩子的标准输出重定向到您自己的标准输出。

I think I might have a solution for you.我想我可能有适合你的解决方案。 Or at least a different method to try.或者至少尝试一种不同的方法。 This example opens the TCL shell as a process.此示例将 TCL shell 作为进程打开。 Then you pump commands to it as though you are on command line.然后你向它发送命令,就好像你在命令行上一样。 Since you said your command line worked, I would think that this would too.既然你说你的命令行有效,我想这也行。 This works for me on Windows with Python 3.7.6 and TCL 8.5.这适用于使用 Python 3.7.6 和 TCL 8.5 的 Windows。

There needs to be a little trickery.需要有一点诡计。 I've found solutions that require threads and all sorts of other overhead to get this job done but they just fell flat.我找到了需要线程和各种其他开销才能完成这项工作的解决方案,但它们只是失败了。 What I came up with is simple and synchronous.我想出的是简单和同步的。

stdout.readline() will block. stdout.readline() 会阻塞。 So if your TCL command doesn't throw anything back, you are dead in the water.所以如果你的 TCL 命令没有返回任何东西,你就完蛋了。

SO, you force something to come back by appending an innocuous TCL puts command that communicates back to the Python script that the job is done.因此,您可以通过附加一个无害的 TCL puts 命令来强制返回一些内容,该命令会与 Python 脚本进行通信,告知工作已完成。

The other trick is that you need to "puts []" the command to force the output back into stdout.另一个技巧是您需要“puts []”命令以强制将输出返回到标准输出。

If you need to source more TCL files, then add those in before you make your final call to what you are trying to run.如果您需要获取更多 TCL 文件的源代码,那么在您最终调用要运行的内容之前添加这些文件。 Whatever you need to do on the cli, you do through this process.无论您需要在 cli 上做什么,都可以通过此过程完成。

My code has this all wrapped in a class with methods.我的代码将这一切都包装在一个带有方法的类中。 I brought them all together here in-line for the example.为了这个例子,我把它们全部放在一起。 Leave the errorInfo code in while debugging and remove as you see fit.在调试时保留 errorInfo 代码,并根据需要删除。

Note: You can also use TCL "info body " to read a script into a string variable and then run each line through the process.注意:您还可以使用 TCL“info body”将脚本读入字符串变量,然后在整个过程中运行每一行。 In essence, if in a Python debugging session, stepping through TCL.本质上,如果在 Python 调试会话中,单步执行 TCL。 Sorry, this does not work too well.抱歉,这不太管用。 The workaround for comments does not work for opening curly braces.注释的变通方法不适用于左花括号。

Hope this helps someone out there.希望这可以帮助那里的人。

EDIT: Using multi-line string handles comment lines.编辑:使用多行字符串处理注释行。

import subprocess

print("------")
shell_path = r"<YOUR PATH TO THE TCL INTERPRETER SHELL>"
tcl_shell = subprocess.Popen(shell_path,
                    stdin =subprocess.PIPE,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    universal_newlines = True,
                    bufsize = 0)

#
# Use ONE of the "command"s below. I have them here in series for exmaples.
#

# Simple
command = r"set temp 5"

# Multiline with error --> This will give an error of course to see the error extraction in action
command = r"""
    set temp 5
    set temp2 [expr temp + 5]
"""

# Multiline working
command = r"""
    set temp 5
    set temp2 [expr $temp + 5]
"""

# More output
command = r"""
    puts "Starting process"
    set temp 5
    puts $temp
    set temp2 [expr $temp + 5]
"""

# Comments handled
command = r"# comment!"

# Be sure to leave the newline to handle comments
tcl_shell.stdin.write(f"""puts [{command}
                                ] \nputs \"tcl_shell_cmd_complete\"\n""")
# NOTE: tcl_shell.stdin.flush() does not seem to be needed. Consider if needed.
result = ""
line = tcl_shell.stdout.readline()
while line != "tcl_shell_cmd_complete\n":
    result += line
    line = tcl_shell.stdout.readline()

print(f"tcl_shell sent:\n{command}")
print(f"tcl_shell result:\n{result}".rstrip())

command_error_check = "puts $errorInfo"
tcl_shell.stdin.write(f"{command_error_check} \nputs \"tcl_shell_cmd_complete\"\n")
resultErr = ""
line = tcl_shell.stdout.readline()
while line != "tcl_shell_cmd_complete\n":
    resultErr += line
    line = tcl_shell.stdout.readline()

print(f"tcl_shell error info:\n{resultErr}")

tcl_shell.stdin.close()
tcl_shell.terminate()
tcl_shell.wait(timeout=0.5)
print("------")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM