Note that your approach actually works, but that the statements declaring the helper functions would be executed twice, once by source
and once after running fun abc
. I would therefore insert a plain exit
statement before the divider.
#!/bin/bash
source <(sed '1,/^# HELPER FUNCTIONS #$/d' "$0")
fun ABC
exit
HELPER FUNCTIONS
fun () {
echo "$@"
}
Also, note that you need to anchor the regular expression, at least to the start of the line, or it will match the line containing the source
command, which in turn would cause the top half of the script (sans sed
) to be executed by source
, which we want to avoid.
Personally, I think this makes the code a bit difficult to read. It also changes some things after the divider. For example, the BASH_SOURCE
array would have an extra filename as its first element, BASH_LINENO
would be offset by the size of the initial part of the script, and any line numbers (and the script name) reported in error messages from that lower part of the script would be difficult to follow up.
Another way to do this, which does not require $0
to have a sensible value, nor calls sed
to modify the script on the fly, is to get into the habit of putting the main body of the script into its own function, possibly called main
, and then call that function at the very end of the script:
#!/bin/sh
main () {
fun ABC
}
fun () {
echo "$@"
}
Call "main" with the arguments given to the script
(even if the function may not use them).
main "$@"
This means that the script would be read to the end by the shell, all functions would be instantiated, and main
could call any function that it may need to do its work, regardless of where in the code those functions are located.
See also the Google style guide for shell scripts, which specifically suggests this, although it says to put the code for the main
function last too, which is not strictly necessary. In fact, it may be worth putting it first to make it easy to find.
Related:
function main { }
and then call it at the very end of the script. Then it comes at the top of the script, but gets invoked after all the helpers are created, so it has access to them. – Quelklef Nov 09 '22 at 18:55