Search code examples
clojureleiningenuberjar

Different slurp behavior with "lein run" and running the uberjar file


I am currently trying to learn Clojure and as part of my practical training, I am implementing the very basic behavior of some of the well known Unix tools like grep, cat, ls and so on.

While implementing cat, I stumbled upon some seemingly strange behavior of slurp. When I run the following code with lein run some-file.txt while some-file.txt lies within the current directory, the content is printed to STDOUT as expected.

(ns cat.core
  (:gen-class))

(defn -main
  "Reads the content of its arguments representing filenames and outputs the
  content in succession."
  [& filenames]
  (doseq [filename filenames]
    (println "Reading" filename) ; Just for debugging purposes
    (print (slurp filename))))

However, if I uberjar the project with

lein compile
lein uberjar

and then cd to target/uberjar to run the standalone JAR with java -jar cat-0.1.0-SNAPSHOT-standalone.jar some-file.txt (with the text file present in that directory), nothing but my debug message gets printed. What I find so strange is that there is no error message being shown, so it seems to me that the file can be found. If I run the JAR with a file that does not exist as parameter, I get an exeption that the specified file cannot be found (as expected).

Because I am working on a Windows machine, a colleague suggested that perhaps Windows shadow files might be a problem. So I tested the program under Linux again and the same behavior occured. So this seems to be a "problem" with my Clojure understanding / my project settings.

My question is: Why is slurp's (or the program's) behavior different when running with lein run and when running the standalone JAR with java -jar cat-0.1.0-SNAPSHOT-standalone.jar and what can I do to solve this?

As the file parameter is not a resource that is compiled into the JAR, there is no need for (slurp (clojure.java.io/resource filename)) if I am not mistaken.


Solution

  • print does not flush the output buffer. You need to use flush after your print statement, or use another println, which flushes on newline:

    (defn -main
      "Reads the content of its arguments representing filenames and outputs the
      content in succession."
      [& filenames]
      (doseq [filename filenames]
        (print (slurp filename))
        (flush)))