昨天我为我的学生写了一个小练习:制作一个反向回声程序。
为了学习新的东西,我尝试实施一个Haskell解决方案。琐碎的main = forever $ interact reverse
不起作用。我浏览了这个问题并制作了一个更正版本:
import Control.Monad
import System.IO
main = forever $ interact revLines
revLines = unlines . map (reverse) . lines
但是这个更正后的版本也不起作用。我阅读了缓冲文档并玩了各种设置。如果我设置NoBuffering
或LineBuffering
,我的程序可以正常工作。最后,我打印出了标准输入和标准输出的默认缓冲模式。
import System.IO
main = do
hGetBuffering stdin >>= print
hGetBuffering stdout >>= print
如果我从 xinetd( echo "test" | nc localhost 7
) 运行我的程序,我必须BlockBuffering Nothing
,但从 cli 我得到了LineBuffering
- 在缓冲方面,xinetd tcp 服务和 cli 程序有什么区别?
- 如果我想用两种运行方法编写工作程序,我是否必须手动设置缓冲?
编辑:谢谢大家的有用答案。
我接受烈焰给出的答案,他给了我一个暗示(3)。我再次浏览了 System.IO 文档并找到了hIsTerminalDevice功能,通过该功能我可以检查句柄的连接。
作为记录,这是我的最后一个程序:
{-# OPTIONS_GHC -W #-}
import System.IO
main = do
hSetBuffering stdin LineBuffering
hSetBuffering stdout LineBuffering
interact revLines
revLines = unlines . map (reverse) . lines
不是特定于Haskell的(例如,标准的C库做同样的事情)。传统上,如果文件描述符对应于终端,则缓冲设置为线路模式,否则设置为块模式。文件描述符类型可以通过isatty(3)
函数检查 - 不确定它是否导出到System.IO
。
是的,如果您依赖它,则需要手动设置缓冲模式。
顺便说一下,您可以通过按cat | ./prog | cat
运行程序来欺骗系统并在命令行中强制块缓冲。
GHC 运行时系统在选择默认缓冲时会尝试变得聪明。如果看起来标准输入和标准输出直接连接到终端,它们将被线路缓冲。如果它们看起来像是连接到其他东西,则它们是块缓冲的。如果要使用不是直接来自终端的逐行输入运行程序,这可能会有问题。例如,我认为cat | your-program
的行为与your-program
不同。
如果我想用两种运行方法编写工作程序,我是否必须手动设置缓冲?
是的。