How to include all files inside a folder using clojure.
Here is my code:
(defn LoadFiles[]
(include "utils")
)
(LoadFiles)
But the above code is not working.
As far as i know, there's no include in clojure (correct me if i'm wrong).
You should use use or require for that.
This one should probably work (for all the .clj files in utils top level, but you can easily extend it to be recursive):
(defn list-sources [path]
(map #(str path "." (second (re-matches #"^(\w+)\.clj$" (.getName %))))
(filter #(.isFile %) (file-seq path))))
(run! #(require (vector (symbol %) :refer :all))
(list-sources (java.io.File. "utils")))
Maybe something like:
(defn load-files [dir]
(doseq [f (file-seq (File. dir))
:when (.isFile f)]
(load-file (.getAbsolutePath f))))
(load-files "utils")
Related
I am new to Clojure.
I have a Clojure app, which exposes log directory on http/s. But unfortunately there are some other files in this same dir. I need to filter files with .log* extensions only.
We had the following code:
(defn get-dir-listing [dir]
(map #(get-all-attribs %) (fs/list-dir dir)))
(defn get-all-attribs [fpath]
{:type (if (fs/directory? fpath)
"Directory"
"File")
:name (.getName fpath)
:modtime (-> (fs/mod-time fpath)
time/from-long)
:size (-> (fs/size fpath)
get-size-in-KB)
})
I changed it to the following:
(defn get-dir-listing [dir]
(map #(get-dirs-log-files-only %) (fs/list-dir dir)))
(defn get-dirs-log-files-only [fpath]
(if (and (not (fs/directory? fpath)) (.contains (.getName fpath) ".log"))
(get-all-attribs fpath)
(if (fs/directory? fpath)
(get-all-attribs fpath))))
(defn get-all-attribs [fpath]
{:type (if (fs/directory? fpath)
"Directory"
"File")
:name (.getName fpath)
:modtime (-> (fs/mod-time fpath)
time/from-long)
:size (-> (fs/size fpath)
get-size-in-KB)
})
;To remove nil
(remove nil? (get-dir-listing "C:\\tmp"))
Does anyone have a better way to resolve it?
You can move the criteria by which you filter the files into a separate function. This will better convey what you are going to do with the listing. Then, you can use filter to apply these criteria.
Also, it is considered a better practice to use Clojure library functions from the relevant namespaces instead of using Java method to work with strings and files (e.g. clojure.string/includes? instead of the String.contains method).
(ns ...
:require [... [clojure.string :as cs]])
(defn is-log-or-dir? [fpath]
(or (fs/directory? fpath)
(cs/includes? (fs/base-name fpath) ".log")))
(defn get-dir-listing [dir]
(map get-all-attribs (filter is-log-or-dir? dir)))
I define a map of functions:
(ns fs
(:require [folder/a :as a]
[folder/b :as b]
[folder/c :as c])
(def functions {:a a/f :b b/f :c c/f})
(doseq [[_ f] functions] (f))
Now I want to add more namespaces within the folder, and I don't want to modify the above code. How can functions be dynamically populated with the f from each namespace in a folder.
First some helpers:
(defn directory
"Get directory from path"
[path]
(clojure.java.io/file path))
(defn file-names
"Get file names of the files in the directory."
[files]
(map (fn [file] (.getName file)) files))
(defn namespace
"Remove the extension from the file name and prefix with folder-name"
[folder-name file-name]
(->> (clojure.string/split file-name #"\.")
(butlast)
(apply str)
(str folder-name ".")
(symbol)))
Then retrieve all namespaces from your folder, you need the path and the folder name:
(def namespaces
(let [names (->> "/path/to/your/folder-name"
directory
file-seq ;; Gets the tree structure of the directory
rest ;; Get rid of the the directory name
file-names)]
(map (partial namespace "folder-name") names)))
Next get the public functions in every namespace via ns-publics:
(def functions
(->> namespaces
(map (juxt keyword (comp vals ns-publics)))
(into {})))
;; => prints {:namespace-key '(fn-a fn-b) :namespace-key2 '(fn-b fn-c)}
Note that this gets a list of all the public functions in a namesapce after the namespace keys.
We can execute the functions as follows:
(doseq [[_ ns-fns] functions
f ns-fns] (f))
Of course, this only works for functions that have an arity of zero. Otherwise you have to pass arguments to f.
I need to send a string that is a path of a directory to a function.how do I do that in Clojure?
I tried doing the following but it didn't work
(defn make-asm-file [d]
(doseq [f (.listFiles d)]
(if
( and (=(str(last (split (.getName f) #"\."))) "vm") (not (.isDirectory f)))
(translate f d))))
(make-asm-file "~\SimpleAdd")
How about the following,
(defn find-files
[regexp directory]
(filter #(and (.isFile %)
(re-find regexp (str %)))
(.listFiles (clojure.java.io/file directory))))
(doseq [f (find-files #".vm$" "~/SimpleAdd")]
(translate f))
In this case (java.io.File. d) would work instead of (clojure.java.io/file d) as well. You could also use file-seq instead of listfiles, but that would include all *.vm files in subdirs.
I am trying to iterate over a list of files in a given directory, and add an incrementing variable i = {1,2,3.....} to their names.
Here is the code I have for iterating through the files and changing each file's name:
(defn addCounterToExtIn [d]
(def i 0)
(doseq [f (.listFiles (file d)) ] ; make a sequence of all files in d
(if (and (not (.isDirectory f)) ; if file is not a directry and
(= '(\. \i \n) (take-last 3 (.getName f))) ) ; if it ends with .in
(fs/rename f (str d '/ i (.getName f)))))) ; add i to start of its name
I don't know how can I increment i as doseq iterates through each file. Alternatively, is there a better loop to use to achieve the desired result?
use file-seq and map-indexed:
(require '[clojure.java.io :as io])
(dorun
(->>
(file-seq (io/file "/home/eduard/Downloads"))
(filter #(re-find #".+\.pdf$" (.getName %)))
(map-indexed (fn [i v] [i v]))))
Change function in map-indexed to rename and you're done.
The sample output for pdf files:
([0 #<File /home/eduard/Downloads/some.pdf>] ...)
This is the first approach off the top of my head. It's not ideal, but certainly more idiomatic than what the question proposes.
(def rename-one-file! [file counter]
(if (and (not (.isDirectory file))
(= ".in" (str (take-last 3 (.getName file)))))
(fs/rename file (file (parent dir)
(str counter (.getName file)))))
(defn iterate-files-with-counter [fn dir]
(loop [counter 0
remaining-files (.listFiles (file dir))]
(let [current-file (first remaining-files)]
(fn file counter)
(recur (+ counter 1) (rest remaining-files))))
(def add-counter-to-ext-in-dir
(partial iterate-files-with-counter rename-one-file!))
Note that the work of actually performing the rename was split off from the work of iterating over the files. Having a large number of small functions is better than than a small number of large functions in general, and making those functions reusable / independent unless you choose to use them together is even better than that.
Let's say I need to create the following directory structure in Clojure:
a
\--b
| \--b1
| \--b2
\--c
\-c1
Instead of doing procedural things like the following:
(def a (File. "a"))
(.mkdir a)
(def b (File. a "b"))
(.mkdir b)
;; ...
... is there a clever way to somehow represent the above actions as data, declaratively, and then create the hierarchy in one fell swoop?
a quick and simple approach would be to make a vector of dirs to create and map mkdir on to it:
user> (map #(.mkdir (java.io.File. %)) ["a", "a/b" "a/b/c"])
(true true true)
or you can specify your dir structure as a tree and use zippers to walk it making the dirs on the way:
(def dirs ["a" ["b" ["b1" "b2"]] ["c" ["c1"]]])
(defn make-dir-tree [original]
(loop [loc (zip/vector-zip original)]
(if (zip/end? loc)
(zip/root loc)
(recur (zip/next
(do (if (not (vector? (zip/node loc)))
(let [path (apply str (interpose "/" (butlast (map first (zip/path loc)))))
name (zip/node loc)]
(if (empty? path)
(.mkdir (java.io.File. name))
(.mkdir (java.io.File. (str path "/" name))))))
loc))))))
(make-dir-tree dirs)
.
arthur#a:~/hello$ find a
a
a/c
a/c/c1
a/b
a/b/c
a/b/b2
a/b/b1
If you are doing a lot of general systems administration then something heavier may be in order. The pallet project is a library for doing system administration of all sorts on physical and cloud hosted systems (though it tends to lean towards the cloudy stuff). Specifically the directory
Another option if you want to easily handle creating recursive directories is to use .mkdirs
user> (require '[clojure.java.io :as io]')
user> (.mkdirs (io/file "a/b/c/d"))
You can use absolute path eg. /a/b/c/d or else it will be created relative to the path you initiated the repl from.
Also handy to check if given path is not an existing directory
user> (.isDirectory (io/file "a/b/c/d"))