Fork me on GitHub

Is there an easy way to get the directory the script is located in? *file* returns the script filename. If you invoke a script as ./foo, *file* will contain /path/to/./foo (which is arguably a bug.) Is there some simple way to get just the /path/to/ part?


yes, with (babashka.fs/parent *file*)


parent returns /path/to/./ in this case (with trailing dot and slash)


you can call (fs/canonicalize ...) on that


or fs/normalize , not sure which


FWIW, when I have a script like dude.clj which prints *file* in normal Clojure I get this:

$ clj -M ./dude.clj
so it seems to behave the same as JVM Clojure which is the goal


Both canonicalize and normalize seem to work properly


Normal Clojure *file* is arguably broken, too, then 🙂


The case of “needing to get the path of the current file” comes up a lot more often writing shell scripts than writing regular Clojure code. Would it be worth adding a Babashka-specific variable here like *script-directory* or the like?


I don't think we're going to add this, but to be honest, I've also never needed this personally. Why do you need it often?


I’m translating Bash and Zsh scripts, which frequently use dirname to find their own location and thus do things to other resources in the same directory (or a subdirectory)

SCRIPT_DIR=$(dirname "$(realpath "$0")")


I personally think this is an anti-pattern to support scripts to be invoked from any directory. I think it's better to just get used to invoking them from the root of a project. It's given me nothing but headaches in bash ;)


given that it might come up though, the *file* approach is good enough imo


I will have to respectfully disagree.. We’ve used this pattern for decades to write portable scripts that can be deployed to a number of different Unix-like environments which we do not directly control, in many cases. They can be invoked using a variety of external invocation mechanisms, and they just work without modification.


with bb/clj you might not need this pattern as often though. I guess the idea is to load other scripts co-located with your script or so?


This particular case is (essentially): 1. Download remote resource using wget into $SCRIPT_DIR/raw/ 2. Use psql to load it into a Postgres database


The script does not necessarily run from any particular directory, and it is not necessarily invoked from within its own directory (by externally managed orchestration tools that are not directly controlled by us)


ok makes sense in that case, yes


True, we could go argue with the people who control the orchestration and try to convince them that our “proper” way is better.. but so far, it’s been a lot easier to just use dirname and have the script find its own location


yeah it makes sense when you don't control who calls the script :)


Right, I would rather focus the considerable energy required to induce them to change on the script itself getting stuff done 🙂


so I'd go with *file* then. please do not that *file* only works on the top level, so I'd so something like this:

(def current-dir (fs/normalize (fs/parent *file*)))
and not:
(defn current-dir  [] (fs/normalize (fs/parent *file*)))


Got it. That seems to work, thanks!


There's also

(System/getProperty "user.dir")
which also works in a repl, and seems to be "pre-absolutized"


although, that's the current directory where you started your script, not the script file location itself

👍 1