One of the features I’d love to have is scripting in Clojure. I would like to create stand-alone scripts that I can run, but are written in Clojure. When I first saw clj, I thought it might be it and actually I am using it in a couple of places; but I’m finding a number of problems in using it effectively.
Shebang.
Obviously, it would be useful to have a script that I can just run and not care what it is written in. so it should start with a #! /usr/bin/env clj - but - being said script a Clojure file, it does not work.
External deps file
I am currently living with a top deps.edn file at the root of my project, but it end up being a free-for-all where a lot of jars live. It would be better to have a way to specify which deps we need in the script itself.
Scripting conveniences
It would be nice to integrate a simpler version of https://github.com/clojure/tools.cli so you can easily declare params in.
Maybe I’m using the wrong tool for the job, or maybe I should just write a shell script that strips the first lines starting with a #, builds an ad-hoc deps.edn and runs clj behind the scenes.
What do you think? anybody else having similar concerns?
PS. Scripts plus Sqlite end up being quite powerful for a lot of small tasks…
FWIW, you actually can start a Clojure file with #! /usr/bin/env clojure and it will work fine (additionally you don’t need to use a .clj extension on the file being run). You can also pass the contents of the deps.edn as a parameter to clojure via the -Sdeps {:deps {...}} command-line option. Unfortunately there’s no portable way to combine these two options since env doesn’t work the same way across platforms.
I’m trying to write my scripts in Clojure tool with new deps and CLI. But instead of using it globally with shebang, I prefer running projects locally based on my habits from Node.js projects.
I did that in my workflow recently by adding cli/ folder and adding it to :paths ["cli/"]. Then I can call from command line:
clj -m build.task-name
Not short enough but available for my case. Then combining with sh and I got my scripts:
Clojure isn’t particularly well suited to scripting, both because of how deps are managed and the rather slow startup time. The easiest way I know of to get the kind of experience you seem to be looking for is with Boot’s scripting support. I’ve got some Clojurescript code that I compile and then run using node to generate graphs and such, but really I’m more likely to use another language for that sort of thing.
I believe Planck might be a better fit for this: http://planck-repl.org/. It compiles and executes Clojurescript in Node, which has a much faster startup time. It also includes some utilities specifically for shell scripting.
Like others have said, boot natively supports shebang with dependency scripts. Or if you prefer lein, this is what I use: https://github.com/hypirion/inlein
I have been using the #!/usr/bin/env clojure shebang line in a no-dependencies script and have been happy enough with the current startup time.
This is what I am seeing:
$ echo $'#!/usr/bin/env clojure\n(println "hello!")' > hello
$ chmod +x hello
$ time ./hello
hello!
real 0m0.881s
user 0m1.808s
sys 0m0.060s
So now I’m curious about the startup time issue. Are those 0.88 seconds unacceptable for most people? Or does the startup time increase a lot when you start adding lots of dependencies?
The time takes to run Clojure alone is acceptable, given that rsyncing assets takes many seconds. If you are compiling front-end projects, it would take more than 30s. Normally I can start Clojure in less than 5s.
Update: I am using scripting a lot lately, using clj and direct git links to our company repos for complex things/dependencies. It’s not real quick, but from the command line is more than acceptable on an old MBP.
I lately tackled the parameter passing issue, so I get scripts that are self-documenting and I can just call them without parameters to know what they are supposed to be doing.
On windows .net, Clojure-clr is suitable for writing scripts as a better powershell. You can just put the dependencies in the same directory as clojure.main.exe.
Shadow-cljs is really good at importing npm packages and the dev experience is really really good (hot reload etc…). My scripts are in clojurescript and at the end of the day I have a js file that starts-up really fast and does the job.
#!/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" "$@"
)
;; Clojure code....