-
-
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 | |
$ |
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.
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: