As part of repository maintenance, I often use a script to delete a group of directories, even if those directories are not empty. It looks something like this:
(require '[clojure.java.io :as io]) . . . (defn delete-children-recursively! [f] "Given a file, delete it. If the file is a directory delete its contents first. This version does not handle symlinks." (when (.isDirectory f) (doseq [f2 (.listFiles f)] (delete-children-recursively! f2))) (when (.exists f) (io/delete-file f))) (defn delete-clean-targets! [v] "Given a vector of directory names (strings), delete the directories and any files they contain." (doseq [dir-name v] (let [dir-file (io/file dir-name)] (delete-children-recursively! dir-file)))) (delete-clean-targets! clean-targets) (System/exit 0)
clean-targets is defined as a vector of directory names. When passed to the
delete-clean-targets! function, all of those directories and their contents are removed.
But if any of the directories contain a symlink, it will not be deleted, and the directory where it resides will not be deleted either since it will not be empty.
Files class provides a way to handle things such that the file can be deleted even if it is a symlink. The revised function looks something like this:
(import [java.nio.file Files LinkOption Path Paths]) . . . (defn delete-children-recursively! [f] "Given a file, delete it. If the file is a directory delete its contents first. This version handles symlinks by deleting the link, not the file it links to." (when (.isDirectory f) (doseq [f2 (.listFiles f)] (delete-children-recursively! f2))) (let [file-name (.getAbsolutePath f) fp (Paths/get file-name (into-array String ))] (try (when (Files/exists fp (into-array [java.nio.file.LinkOption/NOFOLLOW_LINKS])) (Files/delete fp)) (catch Exception e (println "Problem trying to delete " fp) (println "e: " e)))))
There are a couple of interesting changes here, particularly the handling of optional arguments. For interoperability, those arguments get packed up into a native Java array as shown in the call to
Files/exists with the inclusion of the
NOFOLLOW_LINKS argument. It is also required when there are no additional argument, such as the call to
Note that using the
NOFOLLOW_LINKS option means that only the symlink will be deleted, not the file that it points at.