1

我正在尝试通过 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"

这就是我切换到子进程的原因。至少它不会给我错误!

知道如何通过 python 运行带有内部所需包的 tcl 脚本吗?

谢谢

4

3 回答 3

2

要从 using 获取输出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 脚本或者你的路径有一些问题。

于 2013-01-10T17:57:23.327 回答
1

的重点subprocess.Popen是标准通道的重定向,因此您可以以编程方式处理输出,而不是在您自己的标准输出中看到它。你试过处理吗?如何?

也许您根本不需要重定向:那么os.system("tclsh tcltest.tcl")就足够了。或者可能subprocess.Popen对您有其他好处——然后弄清楚如何禁用重定向,或者如何将孩子的标准输出重定向到您自己的标准输出。

于 2013-01-10T17:56:52.847 回答
0

我想我可能会为您提供解决方案。或者至少尝试一种不同的方法。此示例将 TCL shell 作为进程打开。然后你向它发送命令,就好像你在命令行上一样。既然你说你的命令行有效,我认为这也是。这适用于我在 Windows 上使用 Python 3.7.6 和 TCL 8.5。

需要有一点诡计。我找到了需要线程和各种其他开销来完成这项工作的解决方案,但它们只是失败了。我想出的是简单和同步的。

stdout.readline() 将阻塞。因此,如果您的 TCL 命令没有返回任何内容,那么您就死定了。

因此,您通过附加一个无害的 TCL puts 命令来强制返回某些内容,该命令将工作完成的 Python 脚本通信回。

另一个技巧是您需要“放置 []”命令以强制输出回到标准输出。

如果您需要获取更多 TCL 文件,请在对您尝试运行的文件进行最终调用之前添加这些文件。无论您需要在 cli 上做什么,都可以通过此过程完成。

我的代码将这一切都包装在一个带有方法的类中。我将它们全部集中在一起作为示例。在调试时保留 errorInfo 代码,并在您认为合适的情况下删除。

注意:您还可以使用 TCL “info body” 将脚本读入字符串变量,然后运行该过程的每一行。本质上,如果在 Python 调试会话中,单步执行 TCL。抱歉,这不太好用。注释的解决方法不适用于打开花括号。

希望这可以帮助那里的人。

编辑:使用多行字符串处理注释行。

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("------")
于 2020-05-05T21:53:51.280 回答