Tcl 中子解释器的 IO 处理

IO handling for Child Interpreters in Tcl

在尝试使用子解释器时,我注意到下面的代码能够关闭标准输出,然后改为写入文件。

close stdout
set file [open log.txt w]
puts "hello" # prints it to a file

但是,这对儿童口译员不起作用

interp create foo
foo eval {close stdout}
foo eval {set file [open log.txt w]}
foo eval {puts "hello"} #Error: can not find channel named "stdout"

我正在尝试找到一种方法,既不关闭标准输出,又将输出重定向到第三方工具中的文件。我在这里错过了什么?

TIA

首先,close stdout;open 技巧在 child 解释器中不起作用的原因是 parent 解释器 stdout 打开,所以在 child 中关闭它只会让 child 失去对它的访问权限,它实际上并没有关闭底层文件描述符,因此 OS 不会t 在那个插槽中打开新的文件描述符。

如果您使用多线程,您也会看到这种效果。 (频道是 reference-counted 个实体,而且非常 thread-aware。)

有两种简单的方法来处理 third-party 工具:

  1. 运行 子进程中的工具,当您拥有 exec(或 open |…)中的所有重定向工具时。

  2. 替换 puts 命令,以便您可以在脚本级别 拦截写入 并做正确的事情。

     rename puts _real_puts
     proc puts args {
         global accumulator
         if {[llength $args] == 1} {
             append accumulator [lindex $args 1] "\n"
             return
         } elseif {[llength $args] == 2 && [lindex $args 1] eq "-nonewline"} {
             append accumulator [lindex $args 1]
             return
         }
         tailcall _real_puts {*}$args
     }
    

    如果该工具明确使用 stdout

    ,您可能需要比这更复杂一些

从 C 级别拦截对 Tcl stdout 通道的写入是可能的,但需要类似 this.

的东西

从 C 级别拦截对标准输出文件描述符的直接写入通常是不可能的,当然也不容易。您需要弄乱 dup()dup2() 系统调用。