Haskell:如何使用System.Process与外部程序进行标准输入输出交互?
解决Haskell与rot-N子进程管道通信挂起问题
你的代码挂起无响应主要有三个核心问题,对应的关键解决操作如下:
1. 修正错误的IO语法
原代码里的putStrLn <<- hGetLine hout是完全错误的写法——Haskell的绑定运算符<-是把右侧IO操作的结果绑定到左侧变量,正确的读取并打印子进程输出的写法应该是先读取行,再打印:
line1 <- hGetLine hout putStrLn line1
或者用链式调用:hGetLine hout >>= putStrLn。
2. 处理子进程输出缓冲(最关键的挂起因)
默认情况下,当子进程的stdout指向管道而非终端时,系统会启用全缓冲——子进程输出的内容会先存在内存缓冲区,攒够一定量才会写入管道。这会导致父进程的hGetLine一直等待数据,直接挂起。
解决方法是在父进程中手动设置子进程的输出管道为行缓冲,让每一行输出都立即写入管道:
hSetBuffering hout LineBuffering
同时建议给输入管道也设置行缓冲,避免输入数据滞留:
hSetBuffering hin LineBuffering
3. 避免死锁:读取子进程后续输出
当你向子进程发送数据后,子进程大概率还有后续输出。如果父进程直接调用waitForProcess而不读取这些输出,子进程会因为输出缓冲区被填满而阻塞,父进程等子进程结束、子进程等父进程读输出,形成死锁。
解决方法是在等待子进程结束前,读取并打印子进程的所有剩余输出:
hGetContents hout >>= putStr
修正后的完整代码
import Data.Array import System.Process import System.IO import Text.Printf -- 示例decrypt实现,可替换为你的实际代码 decrypt :: Int -> String -> String decrypt n = map (\c -> if c >= 'a' && c <= 'z' then chr ((ord c - ord 'a' + n) `mod` 26 + ord 'a') else c) main = do -- 创建带管道的子进程 (Just hin, Just hout, _, ph) <- createProcess (shell "./rot-N") { std_in = CreatePipe, std_out = CreatePipe } -- 设置管道为行缓冲,解决全缓冲导致的挂起 hSetBuffering hin LineBuffering hSetBuffering hout LineBuffering -- 正确读取并打印子进程前3行输出 line1 <- hGetLine hout putStrLn line1 ciphertext <- hGetLine hout line3 <- hGetLine hout putStrLn line3 putStrLn ciphertext -- 生成所有解密结果 let decryptedStrings = listArray (0,25) $ zipWith decrypt [0..25] $ repeat ciphertext -- 打印解密选项 mapM_ (uncurry $ printf "%02d: %s\n") $ assocs decryptedStrings putStrLn "Select the correct string" number <- getLine >>= return . (read :: Int) -- 发送选中的结果给子进程,强制刷新缓冲区确保数据立即发送 hPutStrLn hin $ decryptedStrings ! number hFlush hin -- 读取并打印子进程后续所有输出,避免死锁 putStrLn "\n--- rot-N后续输出 ---" hGetContents hout >>= putStr -- 关闭管道并等待子进程结束 hClose hin hClose hout _ <- waitForProcess ph return ()
内容的提问来源于stack exchange,提问作者zpepper
相关产品推荐
相关产品推荐

