66

How can I create a list out of all of the files in a specific directory in Clojure? Do I have to resort to calling Java or can Clojure handle this natively?

Mike Crittenden
  • 5,779
  • 6
  • 47
  • 74

7 Answers7

112

Use file-seq function.

Usage example:

(def directory (clojure.java.io/file "/path/to/directory"))
(def files (file-seq directory))
(take 10 files)
Brad Koch
  • 19,267
  • 19
  • 110
  • 137
ffriend
  • 27,562
  • 13
  • 91
  • 132
  • 1
    Do you have a code sample anywhere? I'm having a lot of trouble finding one, and the API doc you linked to isn't very explanatory. Clojure newbie here. – Mike Crittenden Dec 19 '11 at 20:11
  • 5
    `file-seq` is itself a nice code example of `tree-seq` which is useful in accessing any hierarchical data structure. – Alex Stoddard Dec 20 '11 at 14:31
  • Be sure to see the [docs on the java.io.file](https://docs.oracle.com/javase/7/docs/api/java/io/File.html) objects which file-seq returns. – Brad Koch Jan 15 '16 at 04:19
  • 7
    Technically, the OP didn't say they wanted files listed recursively, which (along with the directories themselves) is what you'll get with `file-seq`. If that's not what they wanted, `.listFiles` plus a check for directories is the better option. – neverfox Jan 07 '17 at 22:20
34

Clojure was designed to embrace the Java platform, and this is one area where Clojure does not provide its own API. This means that you probably have to dive into Java, but the classes you have to work with are perfectly usable directly from Clojure.

The one class you should read about in the javadocs is java.io.File, which represents a file path.

http://docs.oracle.com/javase/6/docs/api/java/io/File.html

The .listFiles instance method returns an array (which you can use as a seq) of File objects – one for each entry in the directory identified by the File instance. There are other useful methods for determining whether a File exists, is a directory, and so on.

Example

(ns my-file-utils
  (:import java.io.File))

(defn my-ls [d]
  (println "Files in " (.getName d))
  (doseq [f (.listFiles d)]
    (if (.isDirectory f)
      (print "d ")
      (print "- "))
    (println (.getName f))))

;; Usage: (my-ls (File. "."))

Constructing File Objects

The constructor of File can sometimes be a bit inconvenient to use (especially when merging many path segments in one go) and in this case Clojure provides a useful helper function: clojure.java.io/file. As its arguments it accepts path segments as strings or files. The segments are joined with the correct path separator of the platform.

http://clojuredocs.org/clojure_core/clojure.java.io/file

Note: Clojure also provides the file-seq function which returns a preorder walk (as a seq) though the file hierarchy starting at the given file.

raek
  • 2,126
  • 17
  • 17
20

Usually, when we say that we want to list directory, we mean that we want to get file names or paths, so ->

Simplest way to list directory:

(seq (.list (clojure.java.io/file ".")))

If you want to list it recursive, then:

(map #(.getPath %)
    (file-seq (clojure.java.io/file ".")))
Klay
  • 45
  • 2
  • 6
twistsm
  • 221
  • 2
  • 3
  • first time I see best answer on the bottom. Also, I like how the answer explains what is meant in English "to list directory" -- to return a list, not a tree of files. – xealits Jul 22 '17 at 14:21
  • If you only want *files*, then isFile filter can be added (->> "./src/dir/of/interest" clojure.java.io/file file-seq (filter #(.isFile %)) (map #(.getPath %))) – Shivek Khurana Mar 31 '18 at 16:51
14

Also check out the fs library.

It may not be worth pulling in the extra dependency if you just need a list of files in a directory, but there are many useful utility functions there, for example for:

  • Creating directory structures
  • Copying, deleting, moving
  • Checking and changing permissions
  • Splitting and normalizing paths
  • Creating temporary files and directories
  • Globbing
  • Working with zip and tar files
Christian Berg
  • 14,246
  • 9
  • 39
  • 44
13
(use 'clojure.java.io)
(-> "/tmp" file .listFiles)

The latter expression is an array of File-objects returned from the method listFiles, called on the file-object created from the path "/tmp". This is a fancy way to write:

(.listFiles (file "/tmp"))
Michiel Borkent
  • 34,228
  • 15
  • 86
  • 149
6

To make the modified code match the functionality of original sample code you should add the call to get the file names, like this.

(def directory (clojure.java.io/file "/path/to/directory"))
(def files 
    (for [file (file-seq directory)] (.getName file)))
(take 10 files)
larry
  • 61
  • 1
  • 1
1

A macro to take care of list the files in directory - recursively or just in the parent directory. It can also search files with specific ending extension.

    ;; lists all the files recursively inside the directory passed as d like "."
(defmacro my-rec-list
  "generate the recursive list" {:added "1.0"}
  [d str rec]
  `(->> ~d
       clojure.java.io/file
       ~(if (= rec "rec")
          `file-seq
          `(.listFiles)) 
       ~(cond 
          (= str "file")  `(filter #(.isFile %))
          (= str "dir")   `(filter #(.isDirectory %))
          :always         `(filter #(.matches (.getPathMatcher 
                          (java.nio.file.FileSystems/getDefault)
                          ~str) (.getFileName (.toPath %)))))
       (mapv #(.getAbsolutePath %))))


(defn my-rec-ls-file 
  "generate the recursive list for only files in a directory"
  [d ] 
  (my-rec-list d "file" "rec"))
(defn my-rec-ls-dir 
  "generate the recursive list for only directory"
  [d ] 
  (my-rec-list d "dir" "rec"))
(defn my-rec-ls-jpg 
  "generate the recursive list for files and directory"
  [d ] 
  (my-rec-list d "glob:*.jpg" "rec"))
(defn my-ls-file 
  "generate the recursive list for only files in a directory"
  [d ] 
  (my-rec-list d "file" "norec"))
(defn my-ls-dir 
  "generate the recursive list for only directory"
  [d ] 
  (my-rec-list d "dir" "norec"))
(defn my-ls-jpg 
  "generate the recursive list for files and directory"
  [d ] 
  (my-rec-list d "glob:*.jpg" "norec"))

It leads to different function which can be called with directory to get the list of files, directory or matching extension file - recursively or non-recursively. Example is -

(def directory (clojure.java.io/file "/Path/to/Directory"))
(my-rec-ls-jpg directory) ;; find all jpg file recessively in directory
(my-ls-jpg directory) ;; find jpg file in current directory