Search code examples
bashfunctionsudo

How can I execute a bash function using sudo?


I tried exporting the function and then executing it with bash, but that doesn't work:

$ export -f my_func
$ sudo bash -c 'my_func' 
bash: my_func: command not found

If I try to run the function with bash without sudo (bash -c 'my_func'), it works.

Any idea?


Solution

  • Starting from the answer of bmargulies, I wrote a function to cover this issue, which basically realizes his idea.

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
    # EXESUDO
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
    #
    # Purpose:
    # -------------------------------------------------------------------- #
    # Execute a function with sudo
    #
    # Params:
    # -------------------------------------------------------------------- #
    # $1:   string: name of the function to be executed with sudo
    #
    # Usage:
    # -------------------------------------------------------------------- #
    # exesudo "funcname" followed by any param
    #
    # -------------------------------------------------------------------- #
    # Created 01 September 2012              Last Modified 02 September 2012
    
    function exesudo ()
    {
        ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
        #
        # LOCAL VARIABLES:
        #
        ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
        
        #
        # I use underscores to remember it's been passed
        local _funcname_="$1"
        
        local params=( "$@" )               ## array containing all params passed here
        local tmpfile="/dev/shm/$RANDOM"    ## temporary file
        local content                       ## content of the temporary file
        local regex                         ## regular expression
        
        
        ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
        #
        # MAIN CODE:
        #
        ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
        
        #
        # WORKING ON PARAMS:
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        
        #
        # Shift the first param (which is the name of the function)
        unset params[0]              ## remove first element
        # params=( "${params[@]}" )     ## repack array
        
        
        #
        # WORKING ON THE TEMPORARY FILE:
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        
        content="#!/bin/bash\n\n"
        
        #
        # Write the params array
        content="${content}params=(\n"
        
        regex="\s+"
        for param in "${params[@]}"
        do
            if [[ "$param" =~ $regex ]]
                then
                    content="${content}\t\"${param}\"\n"
                else
                    content="${content}\t${param}\n"
            fi
        done
        
        content="$content)\n"
        echo -e "$content" > "$tmpfile"
        
        #
        # Append the function source
        echo "#$( type "$_funcname_" )" >> "$tmpfile"
        
        #
        # Append the call to the function
        echo -e "\n$_funcname_ \"\${params[@]}\"\n" >> "$tmpfile"
        
    
        #
        # DONE: EXECUTE THE TEMPORARY FILE WITH SUDO
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        sudo bash "$tmpfile"
        rm "$tmpfile"
    }
    



    Example of usage:
    running the following snippet

    #!/bin/bash
    
    function exesudo ()
    {
        # copy here the previous exesudo function !!!
    }
    
    test_it_out ()
    {
        local params=( "$@" )
        echo "Hello "$( whoami )"!"
        echo "You passed the following params:"
        printf "%s\n" "${params[@]}" ## print array
    }
    
    echo "1. calling without sudo"
    test_it_out "first" "second"
    
    echo ""
    echo "2. calling with sudo"
    exesudo test_it_out -n "john done" -s "done"
    
    exit
    



    Will output

    1. calling without sudo
      Hello yourname!
      You passed the following params:
      first
      second

    2. calling with sudo
      Hello root!
      You passed the following params:
      -n
      john done
      -s
      foo



    If you need to use this in a shell calling a function which is defined in your bashrc, as asked with a similar question on serverfault by another user, then you have to put the previous exesudo function on the same bashrc file as well, like the following:

    function yourfunc ()
    {
    echo "Hello "$( whoami )"!"
    }
    export -f yourfunc
    
    function exesudo ()
    {
       # copy here
    }
    export -f exesudo
    



    Then you have to logout and login again or use

    source ~/.bashrc
    



    Finally you can use exesudo as follow:

    $ yourfunc
    Hello yourname!
    
    $ exesudo yourfunc
    Hello root!