【发布时间】:2018-12-07 00:14:02
【问题描述】:
编辑:为清晰起见,修改了原始示例和替代解决方案框架。
行缓冲行为可能与 Tcl 8.6 中的预期行为不同。以下代码块没有任何输出,除非“chan close”行未注释:
set data {one two four}
set stream [open |[list cat -n] r+]
chan configure $stream -buffering line
chan puts $stream "$data\n"
chan puts $stream "\n"
chan flush $stream
#chan close $stream write
set out [chan read $stream]
puts "output: $out"
chan close $stream
所以这个简单的解决方案不适用于交互式 I/O,这可能与管道两端的同步问题有关。
使用通道事件结构(例如,基于http://www.beedub.com/book/2nd/event.doc.html)似乎更可取:
proc chanReader { pipe } {
global extState
while 1 {
set len [chan gets $pipe line]
if { $len > 0 } {
puts "<< $line."
continue
} else {
if { [chan blocked $pipe] } {
set extState 1
return
} elseif { [chan eof $pipe] } {
set extState 2
return
}
}
}
}
set data {one two foure}
set timeout 5000
#set stream [open [list | cat -n] r+]
#set stream [open [list | ispell -a] r+]
set stream [open [list | tr a-z A-Z] r+]
#set stream [open [list | fmt -] r+]
chan configure $stream -blocking 0 -buffering line
set extState 0
chan event $stream readable [list chanReader $stream]
foreach word $data {
puts "> $word\n"
chan puts $stream "$word\n"
chan flush $stream
#chan close $stream write
set aID [after $timeout {set extState 3}]
vwait extState
if { $extState == 1 } {
# Got regular output.
after cancel $aID
puts "Cancel $aID."
continue
} elseif { $extState == 2 } {
puts "External program closed."
chan close $stream
exit 2
} elseif { $extState == 3 } {
puts "Timeout."
chan close $stream
exit 3
}
}
puts "End of task."
chan close $stream
exit 0
此代码片段适用于“cat -n”和“ispell -a”外部程序(注释行),但仍无法与其他外部程序一起使用。例如,它不适用于上面的“tr a-z A-Z”和“fmt”示例。
如果上面的“chan close $stream write”行没有注释,我们会收到来自外部程序的输出,但这会终止与它的交互。如何可靠地(交互地)连接到这些外部程序?
【问题讨论】:
-
那么,为什么“chan close $stream write”命令被注释掉了?我想你一直在尝试,还有更多要告诉我们的。
-
并且
cat命令将具有输入和输出缓冲。除非你能控制管道两端的行为,否则我认为什么都不能保证。 -
是的,我想将此代码片段用于来自外部程序的连续读/写操作,并带有行缓冲通道(管道)。我不想一次写完所有东西,关闭频道,最后从外部程序中读取。我宁愿保持管道打开,并根据需要从中读取。因此我的问题
-
您可以尝试在 8.6 上使用
chan pipe看看是否有任何改变,但是 8.6 在这方面的语义应该与以前的版本相同(除了那些不能半关闭任何东西)。 -
您可能应该注意到,在您的解决方案中,在 Reader proc 中您设置了一个局部变量“done”,但主执行线程正在一个 global 变量上等待“完毕”。在 Reader 中,您要么想要添加一行“global done”,要么将引用更改为“set ::done 1”以强制使用全局命名空间。
标签: tcl