haskell - 如何在 Haskell 中实现 `cat` ?

标签 haskell conduit

我正在尝试用 Haskell 编写一个简单的 cat 程序。我想采用多个文件名作为参数,并将每个文件按顺序写入 STDOUT,但我的程序只打印一个文件并退出。

我需要做什么才能让我的代码打印每个文件,而不仅仅是传入的第一个文件?

import Control.Monad as Monad
import System.Exit
import System.IO as IO
import System.Environment as Env

main :: IO ()
main = do
    -- Get the command line arguments
    args <- Env.getArgs

    -- If we have arguments, read them as files and output them
    if (length args > 0) then catFileArray args

    -- Otherwise, output stdin to stdout
    else catHandle stdin

catFileArray :: [FilePath] -> IO ()
catFileArray files = do
    putStrLn $ "==> Number of files: " ++ (show $ length files)
    -- run `catFile` for each file passed in
    Monad.forM_ files catFile

catFile :: FilePath -> IO ()
catFile f = do
    putStrLn ("==> " ++ f)
    handle <- openFile f ReadMode
    catHandle handle

catHandle :: Handle -> IO ()
catHandle h = Monad.forever $ do
    eof <- IO.hIsEOF h
    if eof then do
        hClose h
        exitWith ExitSuccess
    else
        hGetLine h >>= putStrLn

我正在运行这样的代码:

runghc cat.hs file1 file2

最佳答案

您的问题是 exitWith 终止整个程序。因此,您不能真正使用 forever 来循环遍历文件,因为显然您不想“永远”运行该函数,直到文件末尾。你可以像这样重写catHandle

catHandle :: Handle -> IO ()
catHandle h = do
    eof <- IO.hIsEOF h
    if eof then do
        hClose h
     else
        hGetLine h >>= putStrLn
        catHandle h

即如果还没有到达 EOF,我们就会递归并读取另一行。

但是,整个方法过于复杂。您可以将 cat 简单地写为

main = do
    files <- getArgs
    forM_ files $ \filename -> do
        contents <- readFile filename
        putStr contents

由于惰性 I/O,整个文件内容实际上并未加载到内存中,而是流入 stdout。

如果您对 Control.Monad 中的运算符感到满意,整个程序可以缩短为

main = getArgs >>= mapM_ (readFile >=> putStr)

关于haskell - 如何在 Haskell 中实现 `cat` ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11475006/

相关文章:

haskell - writeTVar 的不安全版本

haskell - Haskell 中的点管道 ".|"运算符是什么?

multithreading - 有没有办法让管道从多个来源获取数据而不阻塞其中任何一个?

haskell - 将元素列表折叠为元素集合

haskell - 为 Haskell 安装 Yesod 时出现问题

haskell - 康威生命游戏递归阶跃函数

haskell - 如何制作 "branched"导管?

haskell - 管道广播

Haskell 导管 : is it possible to optionally have the result of a source?

haskell - 一个巨大数字的最后一位数字