该目录过早被删除

时间:2017-06-01 21:59:50

标签: linux haskell directory lazy-evaluation tar

我尝试使用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文件?

1 个答案:

答案 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