我尝试使用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文件,因为它可能相当大,我不想将它全部存储在内存中。什么是&#34;惯用的&#34;延迟删除目录的方法,直到生成tar文件?
答案 0 :(得分: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
这将允许您,例如,确定生成的tarfile的大小,而不实际使用它做任何事情,但您必须注意严格评估sink
中的结果:
{-# LANGUAGE BangPatterns #-}
main :: IO ()
main = do size <- listFileTo (\tarcontents ->
let !size = LB.length tarcontents in return size)
print size