When my bash scripts start getting complex, I usually break them up into functions. This applies especially to complex pipes, as a sequence of complicated pipe commands (e.g. containing while-loops) can quickly become hard to read. Even more so when parallelization is wanted, where xargs is very helpful.
I know that I can export functions to a subshell with export -f
, thus in a simple case I can do
export -f myfunction
some-command | xargs -Iline bash -c "myfunction 'line'"
but if the myfunction
depends on other functions this becomes hard to maintain -- every time the function changes such that the functions needed by the subshell for executing myfunction
change, the export statement would have to be changed -- that seems pretty error prone.
Is there some general way to export functions for use by subshells? I was thinking about something along the lines of an "export all defined functions" command, which would then allow a code structure like
main() { ... }
func1 () { ... }
func2 () { ... }
<export all functions>
main "$@"
Your question asks only about exporting functions. This is easy in bash, see below.
Your question title/subject implies using functions in xargs, as though they were a script;
I don't know that xargs can "call" a bash function directly, but you can of course wrap
your use of the exported function(s) in a script called by xargs
, see below.
First, a function to list functions. User functions by default and -v to list all functions:
lsfns () {
case "$1" in
-v | v*)
# verbose:
set | grep '()' --color=always
;;
*)
declare -F | cut -d" " -f3 | egrep -v "^_"
;;
esac
}
Next a function to export all user functions:
exportfns () { export -f $(lsfns); }
or just put export -f $(lsfns)
in your .bashrc
.
Example script doit.sh:
#!/bin/bash
lsfns "$@" # make use of function exported by parent shell :)
Example command line (after chmod a+rx doit.sh
):
echo -v | xargs doit.sh
Compare with
echo "" | xargs doit.sh
EDIT 1: responding further to kdb's comment/answer below ("running into situations where exporting functions does not work at all"):
Export of shell functions is not Posix compatible - i.e. it only works with Bash and presumably other shells such as Zsh, Ksh etc.
That is, in Dash, and "standard" Posix shells not providing "export -f", we cannot export functions, AND if we export a function say in Bash, then run a script which starts with the sh-bang e.g. "#!/bin/dash", that script will NOT be able to use the "exported" functions from the parent shell, since functions exported to the "process environment" by Bash, are not recognised by Dash.
EDIT 2: responding further to OP comment "but if the myfunction
depends on other functions this becomes hard to maintain":
This is probably a situation where one could make good use of the shell source
command (alias "."), e.g.:
. ~/etc/my-functions.sh; myMain ...
And similarly, if you "live" in functions rather than script files, e.g. by calling myMain
when you need to, then the first line of this function can be to source your function library;
since this would be excess overhead in the "running a script regularly" case, myMain
becomes the command-line stub function, which (re)loads your function library, and calls the actuallyDoit
function (which would also be called from inside your script, if you have a script file).
Enjoy
Zenaan