Modulinos in Bash
blog.dnmfarrell.com
blog.dnmfarrell.com
Now the Python devs will say that this is perfectly consistent because the names of modules are determined by the __main__ module and how they’re imported and so the idea that the entry point of a script not being __main__ is preposterous but I don’t think that jives with how people actually write Python code and that “the main module” can actually be “mycompany.app.serverboi” and probably should be for the purposes of logging.
Not entirely obvious.
Light reminder that this would be a problem when the shell got set -e:
[[ "$BASH_SOURCE" == "$0" ]] && hello
Shellcheck to the rescue, use if; then; fi instead which is barely longer. set -e
function hello() {
echo "hello"
}
[[ "$BASH_SOURCE" == "$0" ]] && hello
bar.sh: source foo.sh
hello
then: $ bash bar.sh; echo $?
1
because the [[ ]] exits with nonzero, right hand of && does not execute, so the whole expression exits with nonzero, and thus the errexit condition triggers, bailing out of everything.This also happens when moving set -e from foo.sh (the modulino, as a lib) to bar.sh (the modulino consumer), so since in the modulino you can't be sure which of +e or -e will be, you basically have to pick the strictest for robustness.
It's probably best to ensure that sourcing a source-able file results in a 0 return status if sourcing succeeded even under +e, since the caller may be using something along the lines of source file || { echo "failed to source file" >&2; exit 1; }. And sure, using if-then-fi rather than && can be a perfectly good way of achieving that.
Correct version:
[[ "$BASH_SOURCE" == "$0" ]] && { main "$@" ; exit $?; } || : if [[ "${BASH_SOURCE[0]}" = "${0}" ]]; then
hello "${@}"
exit $?
fi. . # exit when sourcing [ "$0" == "${BASH_SOURCE[0]}" ] || return 0
echo "Tests for [$0]" . .
this way you'll wind up able to load/source globs of reusable self testing code into the global namespace with ease.
add a [ -n "$1" ] && { undef and exit } block at the top of the file where you document the names you're using as you would in a header.c and packaging your code for reuse becomes easy.
at last count i have 19 such modules in /ops/bash. they are immensely useful to help me remember this atrocious language :)
[ -n "$_MY_FILE" ] && return || readonly _MY_FILE=1