我最近asked a question about reopening stdin in C通过EOF之后,现在希望在使用Tcl时具有相同的行为。
我似乎找不到C clearerr
会做的Tcl指令。如何一次将ctrl + d传递给stdin,然后又从Tcl脚本“重新打开” stdin? (使用C编译外部库很容易出错!)
当前使用Windows,因此使用ctrl + z,但我认为它们的工作原理相似,在这种情况下不会有所作为。这是一些示例代码:
set var {}; # declare var to hold the line
gets stdin var; # read a line
if {[string length $var]>0} {puts $var}; # print if read
if {[eof stdin]} { # if end-of-file reached
puts {read from stdin was canceled. reopening just for fun}; # some debug message
puts -nonewline "eof reached for stdin. enter something more to echo: "; flush stdout
# clearerr() ???
gets stdin var
if {[string length $var]>0} {puts $var}
}
编辑:阅读fileevent
,我相信我可以提出一种解决方案,即用户根本不需要输入EOF即可在stdin和GUI控件之间进行转换。
答案 0 :(得分:1)
如何一次将ctrl + d传递给stdin,然后再从Tcl脚本中“重新打开” stdin?
我不确定从Tcl POV看这种期望是否有意义。如果在通道上捕获了[eof],则不会关闭stdin的Tcl通道(除非使用[close]明确地这样做,否则Tcl会完全关闭),因此不需要重新打开它。观看:
proc isReadable { f } {
# The channel is readable; try to read it.
set status [catch { gets $f line } result]
if { $status != 0 } {
# Error on the channel
puts "error reading $f: $result"
set ::DONE 2
} elseif { $result >= 0 } {
# Successfully read the channel
puts "got: $line"
} elseif { [eof $f] } {
# End of file on the channel
puts "end of file; just continue working"
# set ::DONE 1
} elseif { [fblocked $f] } {
# Read blocked. Just return
} else {
# Something else
puts "can't happen"
set ::DONE 3
}
}
fconfigure stdin -blocking false
fileevent stdin readable [list isReadable stdin]
# Launch the event loop and wait for the file events to finish
vwait ::DONE
这只是Tcl文档中的标准片段,也用于How to check if stdin is readable in TCL?中。另外,在How to restart stdin after Ctrl+D?的问题答案和注释中的一些注释也适用于Tcl。如果可以使用stdin的源,请使用open
或seek stdin 0 end
查看Brad的评论。
答案 1 :(得分:1)
我相信我找到了解决此问题的纯TCL方法:将EOF字符更改为Ctrl-Z
以外的其他字符,读取一条虚拟行(以从输入缓冲区中删除Ctrl-Z
)然后将EOF字符重置回Ctrl-Z
。包装过程:
proc clearEOF {} {
fconfigure stdin -eofchar { "\x01" "" }
gets stdin dummy
fconfigure stdin -eofchar { "\x1a" "" }
}
\x01
的选择在某种程度上是任意的:基本上,不可能在Ctrl-Z
旁边的输入缓冲区中进行的任何操作。
注意:仅在带有TCL 8.6.9的Windows 10上进行了测试。
puts "Enter lines then Ctrl-Z <RETURN> to end"
while { [ gets stdin line ] >= 0 } {
puts "Read: $line"
}
puts "Reached EOF"
puts "eof=[eof stdin]"
puts "Enter another line"
puts "gets=[gets stdin line]"
puts "Read: $line"
希望是,在读取了多行以EOF标记(Ctrl-Z
终止的行之后,您可以读取另一行。实际上,不会清除EOF状态,并且对gets
的第二次调用不会等待输入,而是立即返回-1
(= EOF):
Enter lines then Ctrl-Z <RETURN>
Line1
Read: Line1
Line2
Read: Line2
^Z
Reached EOF
eof=1
Enter another line <-- This does not wait
gets=-1
Read:
注意:尽管TCL documentation包括(我的重点):
read ?-nonewline? fileID
从fileID读取所有剩余的字节,然后返回该字符串。如果设置了-nonewline,则最后一个字符(如果是换行符)将被丢弃。 在执行读取命令之前,将清除所有现有的文件结尾条件。
用gets
之类的东西替换set line [ read stdin ]
没什么区别。这两个命令都会立即返回。重复执行任一命令都没有区别:一旦TCL(和/或Windows 1 )认为我们已经达到EOF,我们就会在停留 EOF!
经过一番摸索之后,尝试执行所有我可以发现TCL拥有的文件操作命令,我想到了以下内容:
puts "Enter lines then Ctrl-Z <RETURN>"
while { [ gets stdin line ] >= 0 } {
puts "Read: $line"
}
puts "Reached EOF"
puts "eof=[eof stdin]"
puts "Reset EOF char"
fconfigure stdin -eofchar { "\x01" "" }
puts "eof=[eof stdin]"
puts "Reading dummy line"
puts "gets=[gets stdin line]"
fconfigure stdin -eofchar { "\x1a" "" }
puts "Enter another line"
puts "gets=[gets stdin line]"
puts "Read: $line"
此版本确实的输出 等待更多输入:
Enter lines then Ctrl-Z <RETURN>
Line 1
Read: Line 1
Line 2
Read: Line 2
^Z
Reached EOF
eof=1
Reset EOF char
eof=0 <-- EOF has been cleared
Reading dummy line
gets=1 <-- Read 1 character: the Ctrl-Z
Enter another line
More text <-- Waits for this to be typed
gets=9
Read: More text
我对正在发生的事情的假设是,更改EOF字符 会重置EOF状态(无论这种情况发生在TCL中的 还是Windows中的 / em>“我不确定)。使用不同的EOF标记,我们可以读取包含在输入缓冲区中的Ctrl-Z
的行。 (根据您在Ctrl-Z
的两边输入的内容,通常还会包含行尾标记)。丢弃Ctrl-Z
之后,我们可以将EOF字符 back 重置为Ctrl-Z
,并像往常一样从stdin
进行读取。
1 This issue可能是Windows出错了:一旦Ctrl-Z
进入缓冲区,即使使用clearerr()
,它也始终返回EOF。我读到“ 过去30年来对xplat程序员的又一祸害,Unix上的Ctrl-D和Windows上的Ctrl-Z并不一样。”是,尽管该问题是针对WSL的,问题 在Windows本身中。有趣的是,(在撰写本文时)最后的注释指出“ 已在Windows Insider Build 18890中修复”,但可能仍需要调用clearerr()
。