Что такое метод haskell для копирования каталога
Я считаю, что все больше и больше скриптов в haskell. Но есть некоторые случаи, когда я действительно не уверен, как это сделать "правильно".
например скопируйте каталог рекурсивно (a 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
Но каков рекомендуемый способ скопировать каталог в независимую от платформы?
Я не нашел ничего подходящего для взлома.
Это моя довольно наивная реализация, которую я использую до сих пор:
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.
... но все же для меня такая неуклюжая для такой общей задачи!
Ответы
Ответ 1
Я не мог найти ничего, что делает это в 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
Для этого можно использовать библиотеку Shelly
, см. cp_r
:
cp_r "sourcedir" "targetdir"
Сначала Шелли пытается использовать нативный cp -r
если он доступен. Если нет, то он возвращается к собственной реализации IO
Haskell.
Для получения дополнительной информации о семантике типов cp_r
, см. Этот пост, написанный мной для описания того, как использовать cp_r
со String
и или Text
.
Shelly не зависит от платформы, поскольку полагается на пакет Unix, который не поддерживается в Windows.
Ответ 3
Пакет MissingH
обеспечивает рекурсивный обход каталога, который вы можете использовать для упрощения кода.
Ответ 4
Пакет filesystem-trees
предоставляет средства для очень простой реализации:
import System.File.Tree (getDirectory, copyTo_)
copyDirectory :: FilePath -> FilePath -> IO ()
copyDirectory source target = getDirectory source >>= copyTo_ target
Ответ 5
Я предполагаю, что функция в Path.IO copyDirRecur
с вариантами включения/исключения символических ссылок может быть более новым и поддерживаемым решением. Требуется преобразовать Path x Dir
к файлу в Path x Dir
что достигается с помощью parseRelDir
соответствующего parseAbsDir
, но я думаю, чтобы иметь более точный тип даты, чем FilePath
стоит избегать сложных ошибок во время выполнения.
Ответ 6
Есть также некоторые функции для копирования файлов и каталогов в базовые модули Cabal библиотеки Haskell, в частности Distribution.Simple.Utils в пакете Cabal. copyDirectoryRecursive - один, и есть другие функции около этого в этом модуле.