ctrl + d后如何​​在Tcl中清除stdin?

时间:2018-07-17 19:56:16

标签: tcl stdin

我最近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控件之间进行转换。

2 个答案:

答案 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的源,请使用openseek 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进行读取。


微软的WSL GitHub页面上的

1 This issue可能是Windows出错了:一旦Ctrl-Z进入缓冲区,即使使用clearerr(),它也始终返回EOF。我读到“ 过去30年来对xplat程序员的又一祸害,Unix上的Ctrl-D和Windows上的Ctrl-Z并不一样。”是,尽管该问题是针对WSL的,问题 在Windows本身中。有趣的是,(在撰写本文时)最后的注释指出“ 已在Windows Insider Build 18890中修复”,但可能仍需要调用clearerr()