该目录太早被删除

我试图从目录的内容使用Codec.Archive.Tar创build一个tar文件,但我也想要清理目录一旦tar文件已经创build。 这里有一个小例子来说明我的问题:

 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 

当我从ghci中调用函数listFile ,我回来了

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

我猜是由tar文件被懒惰地生成和目录被严格清理。 因此,在tar文件被实际创build之前,该目录被删除。

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

最简单的解决方案是颠倒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版本把它写到一个tar文件中。

你也可以把它推广到可以从接收器返回值的东西:

 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文件的大小,而不用实际做任何事情,但是您必须小心地严格评估接收器中的结果:

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