【问题标题】:running TCL through python by subprocess, but not giving any output通过子进程通过python运行TCL,但没有给出任何输出
【发布时间】:2012-12-25 04:56:50
【问题描述】:

我正在尝试通过 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 脚本吗?!

谢谢

【问题讨论】:

    标签: python tkinter tcl eval subprocess


    【解决方案1】:

    要使用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 运行的脚本也完全有可能产生错误,但由于您没有明确查找它而没有显示。

    编辑:

    为了防止某些信息在下面的cmets中丢失:

    您可能有几个潜在的错误,或者您可以尝试的事情。

    您的 tcl 脚本本身无法正确导入 teapot,或者 tcl 脚本和 python 脚本之间的某种交互无法正常工作,或者 subprocess.Popen 没有正确找到 @ 987654326@ 来自您的路径的包。

    我会尝试按该顺序调试您的程序。首先确认您的 tcl 脚本在不使用 python 或subprocess.Popen 的情况下可以正常工作,然后直接从命令行运行它(例如,C:\Users\blah tclsh tcltest.tcl

    然后,在确保脚本正常工作后,引入 Python。从我所看到的情况来看,您对 python 的东西没有任何问题,但是您的 tcl 脚本或您的路径有一些问题。

    【讨论】:

    • 导入子进程 >>> p = subprocess.Popen( 'tcltest.tcl', shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) >>> stdout, stderr = p.communicate() >>> print stdout >>> print stderr 它刚刚打开了我的脚本(通过记事本++)
    • @Amir -- 你可能需要像上面例子中那样做['tclsh', 'tcltest.tcl'] 吗?
    • 你是什么意思?我应该将此 ['tclsh', 'tcltest.tcl'] 添加到答案的末尾吗?
    • @Amir -- 我编辑了我的答案。您可以尝试重新运行它并查看会发生什么吗?
    • 导入子进程 >>> 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 can't find package teapot while execution "package require teapot" >>>
    【解决方案2】:

    subprocess.Popen 的全部意义在于重定向标准通道,因此您可以通过编程方式处理输出,而不是在您自己的标准输出中看到它。你试过处理吗?怎么样?

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

    【讨论】:

    • thanks,os.system("tclsh tcltest.tcl") Traceback(最近一次调用最后):文件“”,第 1 行,在 os.system(" tclsh tcltest.tcl") NameError: name 'os' is not defined
    • 您必须先import os,然后才能致电os.system
    • 那么,它的意思是,它通过tcl解释器运行tcl?
    • 导入操作系统 >>> os.system("tclsh tcltest.tcl") 1
    • 不要尝试一次调试所有内容:首先,确保脚本在您手动运行tclsh tcltest.tcl 时(在您的shell 提示符下)正常工作。然后调试 Tcl/Python 交互。而当你想在 Python 中处理脚本的输出时,你将不得不再次使用 subprocess.Popen
    【解决方案3】:

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

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

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

    所以,你通过附加一个无害的 TCL puts 命令来强制返回一些东西,该命令将工作完成的 Python 脚本传达回。

    另一个技巧是你需要“puts []”命令来强制输出回到标准输出。

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

    我的代码将这一切都包装在一个带有方法的类中。我将它们全部集中在一起作为示例。在调试时保留 errorInfo 代码,并根据需要删除。

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

    希望这对那里的人有所帮助。

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

    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("------")
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-16
      • 1970-01-01
      • 2018-12-06
      • 2014-06-14
      • 2019-06-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多