2011-07-24 38 views
15

Tôi thấy mình đang làm nhiều hơn và nhiều kịch bản hơn trong haskell. Nhưng có một số trường hợp mà tôi thực sự không chắc chắn làm thế nào để làm điều đó "đúng".
ví dụ: sao chép một thư mục đệ quy (a la unix cp -r).Cách haskell để sao chép một thư mục

Kể từ khi tôi chủ yếu sử dụng Linux và Mac Os Tôi thường gian lận:

import System.Cmd 
import System.Exit 

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

Nhưng cách khuyến khích để sao chép một thư mục một cách độc lập nền tảng là gì?
Tôi không tìm thấy bất kỳ điều gì phù hợp về tấn công.

Đây là thực hiện thay naiv của tôi, tôi sử dụng cho đến nay:

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 

Mọi góp ý về những gì thực sự là cách để làm điều đó?


Tôi đã cập nhật điều này với các đề xuất của hammar và FUZxxl.
... nhưng tôi vẫn cảm thấy vụng về với tôi vì một nhiệm vụ phổ biến như vậy!

+0

Nếu không có thư viện chuẩn như vậy tồn tại, tại sao không làm một? ;) –

+0

Tôi nghĩ rằng điều này không xử lý các liên kết chính xác. Nó sẽ tái tạo chúng. Xem System.Posix.Files. – hasufell

+0

Mã không hoạt động nếu src và dst không bị phân tách. 'copyDir" A "" A/B "' sẽ lặp vô hạn. Xem thêm https://github.com/yesodweb/Shelly.hs/issues/154 –

Trả lời

4

Tôi không thể tìm thấy bất kỳ thứ gì thực hiện điều này trên Hackage.

Mã của bạn trông khá tuyệt với tôi. Một số ý kiến:

  1. dstExists <- doesDirectoryExist dst 
    

    này không đưa vào tài khoản mà một tập tin với tên đích có thể tồn tại.

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

    Bạn có thể muốn ném một ngoại lệ hoặc trả lại trạng thái thay vì in trực tiếp từ chức năng này.

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

    Kể từ khi bạn không sử dụng paths cho bất cứ điều gì, bạn có thể thay đổi điều này để

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

Bạn cũng có thể xem xét việc viết 'if not srcExists || dstExists sau đó in "không thể sao chép" ' – fuz

+0

hammar và @FUXxxl: cảm ơn các đề xuất của bạn. Tôi đã cập nhật mã! ... mong muốn điều này sẽ được đóng gói trong một số thư viện gọn gàng. – oliver

+0

@oliver: 'return()' ở cuối là bây giờ dư thừa. – hammar

1

Gói cung cấp traversals thư mục đệ quy, bạn có thể sử dụng để đơn giản hóa mã của bạn.

5

Có thể sử dụng thư viện Shelly để làm được điều này, xem cp_r:

cp_r "sourcedir" "targetdir" 

Shelly đầu tiên cố gắng sử dụng có nguồn gốc cp -r nếu có.Nếu không, nó sẽ trở lại bản thực thi Haskell IO.

Để biết thêm chi tiết về loại ngữ nghĩa của cp_r, xem this post do tôi viết để mô tả làm thế nào để sử dụng cp_r với String và hay Text.

+0

Tính đến hôm nay, CP_r của Shelly là buggy.'cp_r "A" "A/B" 'vòng xem, https://github.com/yesodweb/Shelly.hs/issues/154 –

+0

@ andreas.abel Cảm ơn thông báo . Như tôi đã trả lời trên GitHub, tôi không nghĩ rằng nó thực sự bị hỏng, ngoại trừ trong một trường hợp góc rất đặc biệt mà kết quả trong một mesage lỗi bằng cách sử dụng bản địa 'cp-r'. Tôi nghĩ rằng nó nên được cố định trong Shelly, nhưng ngay cả khi nó không phải là cố định tôi nghĩ rằng đây vẫn là giải pháp thực tế nhất cho câu hỏi ban đầu cho hầu hết người dùng. –

1

Gói filesystem-trees cung cấp phương tiện cho việc thực hiện rất đơn giản:

import System.File.Tree (getDirectory, copyTo_) 

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

Tuyệt vời. Nhưng nhìn vào ngày hôm nay (tháng 8 năm 2017), cam kết cuối cùng về gói này là từ năm 2015 và ma trận xây dựng bản hack bị trống. –

Các vấn đề liên quan