通过python的子进程运行TCL,但没有任何输出。

问题描述 投票:1回答:3

我试图通过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>

我不知道它是否有效,因为我没有看到任何东西!我的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()

给我这个错误

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"

这就是为什么我改用subprocess的原因,至少它不会给我错误的提示!

有什么办法可以通过python运行我的tcl脚本与内部所需包?

谢谢你

python tkinter tcl eval subprocess
3个回答
2
投票

要从使用 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

完全有可能是你正在运行的脚本中的 subprocess.Popen 也在产生错误,但由于你没有明确地寻找它,所以没有显示。

编辑。

为了防止一些信息在下面的评论中丢失,

你可能有几个潜在的错误,或者你可以尝试一下。

要么你的tcl脚本本身不能导入 teapot 或 tcl 脚本和 python 脚本之间的某种交互没有正常工作,或 subprocess.Popen 没有正确找到 teapot 包的路径。

我会尝试按照这个顺序调试你的程序。首先确认你的tcl脚本在不使用python或 subprocess.Popen 并直接从命令行运行即可(例如。C:\Users\blah tclsh tcltest.tcl)

然后,在你确定你的脚本工作后,把Python带进来。从我的观察来看,你的python没有任何问题,但你的tcl脚本或者你的路径有问题。


1
投票

整个 subprocess.Popen 是标准通道的重定向,所以你可以用程序处理输出,而不是在自己的标准输出上看到。你试过处理吗?怎么处理的?

也许你根本就不需要重定向:那么 os.system("tclsh tcltest.tcl") 应该够了。或者,也许 subprocess.Popen 对你来说有其他的好处 -- 那就想办法禁用重定向,或者如何将孩子的tddout重定向到你自己的tddout。


0
投票

我想我可能有一个解决方案给你。或者至少有一个不同的方法可以尝试。这个例子将TCL shell作为一个进程打开。然后你就像在命令行上一样向它发送命令。既然你说你的命令行可以工作,我想这也可以。我在 Windows 上用 Python 3.7.6 和 TCL 8.5 工作。

这需要一点小技巧。我已经找到了需要线程和其他各种开销来完成这项工作的解决方案,但它们都很失败。我想出的办法很简单,而且是同步的。

stdout.readline()会阻塞。所以,如果你的TCL命令没有扔回任何东西,你就死定了。

所以,你通过附加一个无害的TCL puts命令来强迫一些东西回来,这个命令可以向Python脚本传达工作已经完成。

另一个技巧是,你需要 "put[]"这个命令来强制输出回到stdout中。

如果你需要源更多的TCL文件,那么在你对你试图运行的东西进行最后的调用之前,将这些文件添加进去。无论你需要在cli上做什么,你都要通过这个过程来完成。

我的代码把这些都封装在一个带有方法的类中。我把它们都集中在一起,在这里排成一行来做例子。调试的时候把errorInfo代码留在里面,看到合适的就去掉。

注意:你也可以使用TCL的 "info body "把一个脚本读到一个字符串变量中,然后把每一行都跑一遍。实质上,如果在Python调试环节中,踩过TCL。 抱歉,这个方法不太好用。注释的变通方法对打开大括号不起作用。

希望能帮到大家。

EDIT:使用多行字符串处理评论行。

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("------")
© www.soinside.com 2019 - 2024. All rights reserved.