I have these functions in my .bashrc:
# This function just untar a file:
untar()
{
tar xvf $1
}
# This function execute a command with nohup (you can leave the terminal) and nice for a low priority on the cpu:
nn()
{
nohup nice -n 15 "$@" &
}
Before testing the nn function, I create a tar:
echo test > test.txt
tar cvf test.txt.tar test.txt
Now what I want to do is:
nn untar test.txt.tar
But only this works:
nn tar xvf test.txt.tar
Here the error in nohup.out:
nice: ‘untar’: No such file or directory
Functions are not first-class citizens. The shell knows what they are, but other commands like find
, xargs
, and nice
do not. To call a function from another program you need to (a) export it to sub-shells, and (b) explicitly invoke a sub-shell.
export -f untar
nn bash -c 'untar test.txt.tar'
You could automate this if you want to make it easier for the caller:
nn() {
if [[ $(type -t "$1") == function ]]; then
export -f "$1"
set -- bash -c '"$@"' bash "$@"
fi
nohup nice -n 15 "$@" &
}
This line deserves an explanation:
set -- bash -c '"$@"' bash "$@"
set --
changes the current function's arguments; it replaces "$@"
with a new set of values.bash -c '"$@"'
is the explicit subshell invocation.bash "$@"
are the arguments to the subshell. bash
is $0
(not used). The outer existing arguments "$@"
are passed to the new bash instance as $1
, $2
, etc. This is how we get the subshell to execute the function call.Let's see what happens if you call nn untar test.txt.tar
. The type -t
check sees that untar
is a function. The function is exported. Then set
changes nn
's arguments from untar test.txt.tar
to bash -c '"$@"' bash untar test.txt.tar
.