-
-
Save ericnormand/6bb4562c4bc578ef223182e3bb1e72c5 to your computer and use it in GitHub Desktop.
| #!/bin/sh | |
| #_( | |
| #_DEPS is same format as deps.edn. Multiline is okay. | |
| DEPS=' | |
| {:deps {clj-time {:mvn/version "0.14.2"}}} | |
| ' | |
| #_You can put other options here | |
| OPTS=' | |
| -J-Xms256m -J-Xmx256m -J-client | |
| ' | |
| exec clojure $OPTS -Sdeps "$DEPS" "$0" "$@" | |
| ) | |
| (println "Hello!") | |
| (require '[clj-time.core :as t]) | |
| (prn (str (t/now))) | |
| (prn *command-line-args*) | |
| (println (.. (Runtime/getRuntime) | |
| totalMemory)) |
| $ cp script.clj ~/bin/cljtest2 | |
| # ~/bin is on my $PATH | |
| $ chmod +x ~/bin/cljtest2 | |
| $ time cljtest2 "Yo" "Hey" 1 3 4 - -ff | |
| Hello! | |
| "2019-03-01T17:22:43.564Z" | |
| ("Yo" "Hey" "1" "3" "4" "-" "-ff") | |
| 268435456 | |
| real 0m2.073s | |
| user 0m6.073s | |
| sys 0m0.297s | |
| $ |
I should mention that "$0" means the currently executing script file. I'm passing the current file to clojure to execute. "$@" adds all of the arguments passed to the script.
I love this.
Strong odds that someone with better shell-fu than me could reads the deps directly from the file!
If you are willing to rely on bash (or zsh), you could use
DEPS=$(<deps.edn)
instead.
That is cool. Thanks for the explanations!
So you have to be a little careful about what you put between the parens on lines 2 and 16. For example I had to add quotes around the URL in this line to make the Clojure reader happy:
#_this shebang genius is from "https://gist.github.com/ericnormand/6bb4562c4bc578ef223182e3bb1e72c5"
Just in case somebody is looking at this and is okay with having separate deps.edn (I do, mainly for cider-jack-in-clj), then this could be simplified to a single string:
":";exec clojure -M -m $(basename $0 .clj)
This should be a first line of a file. From Clojure's point of view, it's a string ":" and then a comment (since it starts with ;), so a no-op practically.
From shell's point of view, if there is no shebang and no pound sign as a first character of a file, then this file is going to be run with /bin/sh. : is an empty command (try it out in your shell), then ; is a command separator and then next command is exec effectively handing control over to clojure.
All this stuff here is to pass -m namespace to clojure, so that it will call function -main in that script. This means you can safely eval this in REPL how many times you want without re-executing initialization code. This is done by $(basename $0 .clj), of course.
Babashka sets "babashka.file" system property to a file being run, which simplifies all that machinery. clojure does nothing similar, so this is the way I came up with. Would love to find out if there is a way like Python's if __name__ == "__main__".
I just figured out a way to make it even more portable by adding a step that install a specific version of Clojure local to the script.
#!/bin/sh
#_(
#_DEPS is same format as deps.edn. Multiline is okay.
DEPS='
{:deps {
clj-http/clj-http {:mvn/version "3.12.3"}
cheshire/cheshire {:mvn/version "5.11.0"}
}}
'
#_You can put other options here
OPTS='
-J-Xms4m -J-Xmx256m
'
if [[ ! -x .local/bin/clojure ]]; then
[[ ! -d .local ]] && mkdir .local
pushd .local
curl -O https://download.clojure.org/install/posix-install-1.11.1.1273.sh
chmod +x posix-install-1.11.1.1273.sh
./posix-install-1.11.1.1273.sh -p $PWD
popd
fi
exec .local/bin/clojure $OPTS -Sdeps "$DEPS" "$0" "$@"
)
(require
'[clj-http.client :as client]
'[clojure.pprint :as pp])
(defn -main [& args]
(pp/pprint (:body (client/get "https://www.example.com" {}))))
(apply -main *command-line-args*)So with this small block added, your only dependency is java.
Mostly the same description I have checked in here: https://github.com/jafingerhut/dotfiles/blob/master/templates/clj-template-README.md but it may have a few more details fleshed out.