2011-07-24 17 views
15

Me encuentro haciendo más y más scripts en haskell. Pero hay algunos casos en los que realmente no estoy seguro de cómo hacerlo "bien".
p. copiar un directorio recursivamente (a la unix cp -r).¿Cuál es la forma de haskell para copiar un directorio?

Desde que todo utilizar Linux y Mac OS por lo general trampa:

import System.Cmd 
import System.Exit 

copyDir :: FilePath -> FilePath -> IO ExitCode 
copyDir src dest = system $ "cp -r " ++ srC++ " " ++ dest 

pero lo que es la forma recomendada para copiar un directorio de una manera independiente de la plataforma?
No encontré nada adecuado en hackage.

Esta es mi aplicación en lugar naiv utilizo hasta el momento:

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 

Cualquier sugerencia de lo que realmente es la manera de hacerlo?


He actualizado esto con las sugerencias de hammar y FUZxxl.
... pero aún así me parece algo torpe para una tarea tan común.

+0

Si no existe dicha biblioteca estándar, ¿por qué no hacer uno? ;) –

+0

Creo que esto no maneja los enlaces simbólicos correctamente. Debería recrearlos. Ver System.Posix.Files. – hasufell

+0

El código no funciona si src y dst no son disjuntos. 'copyDir" A "" A/B "' se repetirá infinitamente. Consulte también https://github.com/yesodweb/Shelly.hs/issues/154 –

Respuesta

4

No pude encontrar nada que haga esto en Hackage.

Tu código se ve bastante bien para mí. Algunos comentarios:

  1. dstExists <- doesDirectoryExist dst 
    

    Esto no tiene en cuenta que un archivo con el nombre de destino podría existir.

  2. if or [not srcExists, dstExists] then print "cannot copy" 
    

    Es posible que desee lanzar una excepción o devolver un estado en lugar de imprimir directamente desde esta función.

  3. paths <- forM xs $ \name -> do 
        [...] 
        return() 
    

    Puesto que usted no está utilizando paths para cualquier cosa, puede cambiar esto a

    forM_ xs $ \name -> do 
        [...] 
    
+0

También podría considerar escribir 'if not srcExists || dstExists luego imprime "no se puede copiar" ' – fuz

+0

hammar y @FUXxxl: gracias por sus sugerencias. ¡Actualicé el código! ... desearía que esto fuera empaquetado en una biblioteca ordenada. – oliver

+0

@oliver: el 'return()' al final ahora es redundante. – hammar

1

El paquete proporciona recorrido recursivo de directorios, los cuales es posible que puedas usarlo para simplificar tu código.

5

Es posible usar la biblioteca Shelly con el fin de hacer esto, vea cp_r:

cp_r "sourcedir" "targetdir" 

Shelly primero intenta utilizar nativa cp -r si está disponible.Si no, se recurre a una implementación nativa de Haskell IO.

Para más detalles sobre la semántica de tipo de cp_r, ver this post escritos por mí se describe cómo utilizar cp_r con String y o Text.

+0

A día de hoy, el cp_r de Shelly tiene errores. "Cp_r" A "" A/B "' loops, https://github.com/yesodweb/Shelly.hs/issues/154 –

+0

@ andreas.abel Gracias por el aviso . Como respondí en GitHub, no creo que esté realmente roto, excepto en un caso de esquina muy especial que da como resultado un mensaje de error usando 'cp -r' nativo. Creo que debería arreglarse en Shelly, pero incluso si no se soluciona, creo que sigue siendo la solución más pragmática para la pregunta original para la mayoría de los usuarios. –

1

El paquete filesystem-trees proporciona los medios para una implementación muy sencilla:

import System.File.Tree (getDirectory, copyTo_) 

copyDirectory :: FilePath -> FilePath -> IO() 
copyDirectory source target = getDirectory source >>= copyTo_ target 
+0

Genial. Pero mirando hoy (agosto de 2017), el último compromiso en este paquete es desde 2015, y la matriz de creación de piratas está vacía. –

Cuestiones relacionadas