目录被过早删除
The directory is being deleted too early
我正在尝试使用 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 文件之前延迟删除目录的 "idiomatic" 方法是什么?
最简单的解决方案是反转 listFile
函数的控制。而不是让它 return 一个惰性的 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
接受一个"sink",一个接受惰性ByteString
并用它执行IO操作的函数。例如,以上版本的 main
将其写入 tarfile。
您还可以将其概括为可以 return 来自接收器的值的东西:
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
:[=20 中的结果=]
{-# LANGUAGE BangPatterns #-}
main :: IO ()
main = do size <- listFileTo (\tarcontents ->
let !size = LB.length tarcontents in return size)
print size
我正在尝试使用 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 文件之前延迟删除目录的 "idiomatic" 方法是什么?
最简单的解决方案是反转 listFile
函数的控制。而不是让它 return 一个惰性的 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
接受一个"sink",一个接受惰性ByteString
并用它执行IO操作的函数。例如,以上版本的 main
将其写入 tarfile。
您还可以将其概括为可以 return 来自接收器的值的东西:
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
:[=20 中的结果=]
{-# LANGUAGE BangPatterns #-}
main :: IO ()
main = do size <- listFileTo (\tarcontents ->
let !size = LB.length tarcontents in return size)
print size