2017-06-01 63 views
2

我想从目录的内容使用Codec.Archive.Tar创建一个tar文件,但是我也想在创建tar文件后清理目录。下面是这证明了我的问题一个小例子:该目录太早被删除

import System.Directory 
import qualified Codec.Archive.Tar as T 

listFile = do createDirectory "dir" 
       createDirectory "dir/dir2" 
       tarfile <- fmap T.write $ T.pack "dir" ["dir2"] 
       removeDirectoryRecursive "dir" 
       return tarfile 

当我调用该函数listFile例如从ghci的范围内,我回来

"*** Exception: dir/dir2: getModificationTime:getFileTimes:getFileStatus: does not exist (No such file or directory) 

,我猜是由tar文件所引起的懒洋洋产生的目录,严格清理。因此该目录在tar文件实际创建之前被删除。

首先,我是否正确地分析了为什么这是失败?如果是这样,我能做些什么来解决这个问题?我不想严格生成tar文件,因为它可能相当大,我不想将它全部存储在内存中。什么是“惯用”的方式来延迟删除目录,直到生成tar文件?

+0

“我不想严格生成tar文件,因为它可能是相当大的,我不希望这一切存储在记忆。”你是否试图将tar文件写入磁盘?你可能正在寻找一个合适的流库(管道,管道,流,io流等等),可能有人已经在流接口中包装了tar操作 – jberryman

+0

我的确想象其中一个库可以解决这个问题,但是我需要花一些时间阅读所有文档并了解正在发生的事情。这有点令人困惑,以前从未在Haskell中处理过流式处理。 –

回答

1

最简单的解决方案是对您的listFile函数进行反转控制。而不是让它返回一个懒惰的ByteString(一旦目录被移除,这将是无用的),让它执行一个IO动作来消耗ByteString,实际上一些东西与它删除目录之前。例如:

import System.Directory 
import qualified Codec.Archive.Tar as T 
import qualified Data.ByteString.Lazy as LB 
import System.IO 

listFileTo :: (LB.ByteString -> IO()) -> IO() 
listFileTo sink = do createDirectory "dir" 
        createDirectory "dir/dir2" 
        tarfile <- fmap T.write $ T.pack "dir" ["dir2"] 
        sink tarfile 
        removeDirectoryRecursive "dir" 

main :: IO() 
main = listFileTo (\tarcontents -> withBinaryFile "my.tar" WriteMode 
        (\h -> LB.hPut h tarcontents)) 

这里,listFileTo采用“水槽”,即需要一个懒惰ByteString,并用它执行一个IO动作的功能。例如,上述版本的main将其写入tarfile。

你也可以概括这东西,可以从水槽返回一个值:

listFileTo :: (LB.ByteString -> IO a) -> IO a 
listFileTo sink = do createDirectory "dir" 
        createDirectory "dir/dir2" 
        tarfile <- fmap T.write $ T.pack "dir" ["dir2"] 
        result <- sink tarfile 
        removeDirectoryRecursive "dir" 
        return result 

这将允许你,例如,确定最终的tar文件的大小,而不实际做任何事的,但你必须照顾到严格考核结果在sink

{-# LANGUAGE BangPatterns #-} 

main :: IO() 
main = do size <- listFileTo (\tarcontents -> 
           let !size = LB.length tarcontents in return size) 
      print size