linuxbashubuntunohupnice

Bash : how to use a function (which is a string param) in an other function


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

Solution

  • 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 "$@"
    
    1. set -- changes the current function's arguments; it replaces "$@" with a new set of values.
    2. bash -c '"$@"' is the explicit subshell invocation.
    3. 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.