我发现自己在haskell中做了越来越多的脚本编写。但在某些情况下,我真的不确定如何“正确”这样做
例如以递归方式复制目录(la unix cp -r
)。
由于我主要使用Linux和Mac Os,我经常作弊:
import System.Cmd
import System.Exit
copyDir :: FilePath -> FilePath -> IO ExitCode
copyDir src dest = system $ "cp -r " ++ src ++ " " ++ dest
但是,以独立于平台的方式复制目录的推荐方法是什么? 我没有找到任何合适的hackage。
这是我到目前为止使用的相当天真的实现:
import System.Directory
import System.FilePath((</>))
import Control.Applicative((<$>))
import Control.Exception(throw)
import Control.Monad(when,forM_)
copyDir :: FilePath -> FilePath -> IO ()
copyDir src dst = do
whenM (not <$> doesDirectoryExist src) $
throw (userError "source does not exist")
whenM (doesFileOrDirectoryExist dst) $
throw (userError "destination already exists")
createDirectory dst
content <- getDirectoryContents src
let xs = filter (`notElem` [".", ".."]) content
forM_ xs $ \name -> do
let srcPath = src </> name
let dstPath = dst </> name
isDirectory <- doesDirectoryExist srcPath
if isDirectory
then copyDir srcPath dstPath
else copyFile srcPath dstPath
where
doesFileOrDirectoryExist x = orM [doesDirectoryExist x, doesFileExist x]
orM xs = or <$> sequence xs
whenM s r = s >>= flip when r
有什么方法可以做到这一点?
我根据hammar和FUZxxl的建议对此进行了更新 ......但是对于这样一个共同的任务,我仍然感觉有点笨拙!
答案 0 :(得分:5)
cp_r "sourcedir" "targetdir"
Shelly首先尝试使用原生cp -r
(如果可用)。如果没有,它将回退到本机Haskell IO
实现。
有关cp_r
的类型语义的更多详细信息,请参阅我撰写的this post,说明如何将cp_r
与String
和/ Text
一起使用。< / p>
答案 1 :(得分:4)
我在Hackage上找不到任何能做到这一点。
您的代码对我来说非常好。一些评论:
dstExists <- doesDirectoryExist dst
这不考虑具有目的地名称的文件可能存在。
if or [not srcExists, dstExists] then print "cannot copy"
您可能希望抛出异常或返回状态,而不是直接从此函数打印。
paths <- forM xs $ \name -> do [...] return ()
由于您没有使用paths
进行任何操作,因此您可以将其更改为
forM_ xs $ \name -> do
[...]
答案 2 :(得分:1)
MissingH
包提供了递归目录遍历,您可以使用它来简化代码。
答案 3 :(得分:1)
filesystem-trees
包提供了一种非常简单的实现方法:
import System.File.Tree (getDirectory, copyTo_)
copyDirectory :: FilePath -> FilePath -> IO ()
copyDirectory source target = getDirectory source >>= copyTo_ target
答案 4 :(得分:1)
我认为Path.IO copyDirRecur
中的函数带有包含/排除符号链接的变体可能是一个更新且维护的解决方案。它需要将文件路径转换为Path x Dir
,分别通过parseRelDir
和parseAbsDir
实现,但是我认为拥有比FilePath
更精确的日期类型值得避免。在运行时跟踪错误。
答案 5 :(得分:0)
在Haskell核心Cabal模块中,特别是Cabal包中的Distribution.Simple.Utils,还具有一些用于复制文件和目录的功能。 copyDirectoryRecursive是其中之一,并且在该模块中还有其他功能。