昨天我为我的学生写了一个小小的xinetd练习:做一个反向回音程序。
为了学习新东西,我试图实现一个Haskell解决方案。琐碎的main = forever $ interact reverse
不起作用。我浏览了this question并制作了更正后的版本:
import Control.Monad
import System.IO
main = forever $ interact revLines
revLines = unlines . map (reverse) . lines
但是这个更正后的版本也行不通。我读了buffering documentation并玩了各种设置。
如果我设置NoBuffering
或LineBuffering
,我的程序可以正常运行。最后,我打印出stdin和stdout的默认缓冲模式
import System.IO
main = do
hGetBuffering stdin >>= print
hGetBuffering stdout >>= print
如果我从xinetd(BlockBuffering Nothing
)运行我的程序,我已经echo "test" | nc localhost 7
但是从cli我已经LineBuffering
编辑:谢谢大家提供的有用答案。
我接受了大火给出的答案,他给了我一个isatty(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
答案 0 :(得分:11)
它不是特定于Haskell(例如标准C库做同样的事情)。
传统上,如果文件描述符对应于终端,则缓冲设置为行模式,否则设置为阻止模式。 isatty(3)
函数可以检查文件描述符类型 - 不确定它是否导出到System.IO
。
是的,如果你依赖它,你需要手动设置缓冲模式。
顺便说一句,您可以通过以cat | ./prog | cat
运行程序来欺骗系统并在命令行中强制阻止缓冲。
答案 1 :(得分:5)
GHC运行时系统在选择默认缓冲时会尝试变得聪明。如果看起来stdin和stdout直接连接到终端,它们将是行缓冲的。如果看起来它们与其他东西相连,则它们是块缓冲的。如果您想要使用不直接来自终端的逐行输入来运行程序,则可能会出现问题。例如,我认为cat | your-program
的行为与your-program
的行为不同。
如果我想用两种运行方法编写一个工作程序,我是否必须手动设置缓冲?
是