2017-06-01 90 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