# A function that recursively copies files and directories

**URL:** <https://racket.discourse.group/t/a-function-that-recursively-copies-files-and-directories/3829>\
**Category:** General\
**Tags:** question, filesystem\
**Created:** [July 3, 2025, 9:34am UTC](https://racket.discourse.group/t/a-function-that-recursively-copies-files-and-directories/3829 "2025-07-03T09:34:40Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![indrjo](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/indrjo/32/1346_2.png) [@indrjo](https://racket.discourse.group/u/indrjo)\
**Post date:** [July 3, 2025, 9:34am UTC](https://racket.discourse.group/t/a-function-that-recursively-copies-files-and-directories/3829/1 "2025-07-03T09:34:40Z")

</div>

I know there is the function `copy-directory/files`, but I want a function that while descending directories performs some tasks before copying (see `actions-on-dir`). Here is the code I have written so far.

```scheme
#lang racket

(provide (prefix-out charon: recursive-copy))

;; Isn't there a builtin function for this?
(define path-basename
  (compose last explode-path))

;; Try to perform an action. If any failure occurs, just print the
;; error and go ahead as if nothing happened.
(define-syntax-rule (try-and-go-on action)
  (with-handlers ([exn:fail?
                   (λ (e) (printf "ERROR: ~a\n" (exn-message e)))])
    action))

;; Copy a file into a given directory. Modification times are
;; considered to avoid copying when the copy is already up to date.
(define (copy-file-to-directory src-file dest)
  (try-and-go-on
   (let* ([name (path-basename src-file)]
          [copy (build-path dest name)])
     (if (file-exists? copy)
         (let ([mt1 (file-or-directory-modify-seconds src-file)]
               [mt2 (file-or-directory-modify-seconds copy)])
           (when (> mt1 mt2)
             (copy-file src-file copy #:exists-ok? #t)))
         (copy-file src-file copy #:exists-ok? #t)))))

(define (ls dir)
  (map path->string (directory-list dir)))

(define (actions-on dir)
  (current-directory dir)
  (let* ([dir-cont (ls dir)]
         [exts (map path-get-extension dir-cont)])
    (cond
      ;; project managed with `make`
      [(member "Makefile" dir-cont)
       (printf "INFO: running \"make clean\" in ~a...\n" dir)
       (system "make clean")]
      ;; Haskell projects
      [(member ".stack-work" dir-cont)
       (printf "INFO: running \"stack purge\" in ~a...\n" dir)
       (system "stack purge")]
      [(member #".cabal" exts)
       (printf "INFO: running \"cabal clean\" in ~a...\n" dir)
       (system "cabal clean")])))

(define (recursive-copy src dest)
  (if (file-exists? src)
      (copy-file-to-directory src dest)
      (begin
        (actions-on src)
        (let* ([name (path-basename src)]
               [new-dest (build-path dest name)])
          (make-directory* new-dest)
          (for-each
           (λ (e) (recursive-copy (build-path src e) new-dest))
           (directory-list src))))))

```

I'm relatively new to Racket: I'm exploring it as I come up with small programs I need for some routine work. Is this decent, or can be made more simple? In particular, there is a function I don't get how it works but I think it might be useful here: the function [fold-files](https://docs.racket-lang.org/reference/Filesystem.html#%28def._%28%28lib._racket%2Ffile..rkt%29._fold-files%29%29).

---

<div class="post-metadata">

**Author:** ![greghendershott](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/greghendershott/32/98_2.png) [@greghendershott](https://racket.discourse.group/u/greghendershott)\
**Post date:** [July 3, 2025, 12:27pm UTC](https://racket.discourse.group/t/a-function-that-recursively-copies-files-and-directories/3829/2 "2025-07-03T12:27:42Z")

</div>

I think you want [`in-directory`](https://docs.racket-lang.org/reference/sequences.html#%28def._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._in-directory%29%29).

(Its doc examples use it with `for/list` to gather results. But using it just for effect, you could just use `for`.)

> ```scheme
> ;; Isn't there a builtin function for this?
> (define path-basename
> (compose last explode-path))
> 
> ```

See [file-name-from-path](https://docs.racket-lang.org/reference/More_Path_Utilities.html#%28def._%28%28lib._racket%2Fpath..rkt%29._file-name-from-path%29%29).

---

<div class="post-metadata">

**Author:** ![benknoble](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/benknoble/32/16_2.png) [@benknoble](https://racket.discourse.group/u/benknoble)\
**Post date:** [July 3, 2025, 12:49pm UTC](https://racket.discourse.group/t/a-function-that-recursively-copies-files-and-directories/3829/3 "2025-07-03T12:49:55Z")

</div>

A tip: parameterize the current-directory so it will be restored when your functions return

---

<div class="post-metadata">

**Author:** ![indrjo](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/indrjo/32/1346_2.png) [@indrjo](https://racket.discourse.group/u/indrjo)\
**Post date:** [July 7, 2025, 6:20pm UTC](https://racket.discourse.group/t/a-function-that-recursively-copies-files-and-directories/3829/4 "2025-07-07T18:20:17Z")

</div>

I know `path-basename` is an unhappy choice... Maybe `path-last` should fit better. The function I want just splits a path and takes the last element. On the other hand, `file-name-from-path` gives `#f` on path that are not syntactically directories.

You two are suggesting to write something like

```scheme
(for ([f (in-directory dir)])
  (do-something-with f))

```

with a `make-parameter` somewhere within `(do-something f)`. What would you do?
