bashsudosu

call a bash function as root but gives 'unexpected end of file'


Why does the last example throw an error but the others work? Bash is invoked in any case.

#!/bin/bash

function hello {
    echo "Hello! user=$USER, uid=$UID, home=$HOME"; 
}

# Test that it works.
hello

# ok
bash -c "$(declare -f hello); hello"

# ok
sudo su $USER bash -c "$(declare -f hello); hello"

# error: bash: -c: line 1: syntax error: unexpected end of file
sudo -i -u $USER bash -c "$(declare -f hello); hello"

Solution

  • It fail because of the -i or --login switch:

    It seems like when debugging with -x

    $ set -x
    $ sudo -i -u $USER bash -c "$(declare -f hello); hello"
    ++ declare -f hello
    + sudo -i -u lea bash -c 'hello () 
    { 
        echo "Hello! user=$USER, uid=$UID, home=$HOME"
    }; hello'
    

    Now if doing it manually it cause the same error:

    sudo -i -u lea bash -c 'hello () 
    { 
        echo "Hello! user=$USER, uid=$UID, home=$HOME"
    }; hello'
    

    Now lets just do a simple tiny change that make it work:

    sudo -i -u lea bash -c 'hello () 
    { 
        echo "Hello! user=$USER, uid=$UID, home=$HOME";}; hello'
    

    The reason is that sudo -i runs everything like an interactive shell. And when doing so, every newline character from the declare -f hello is internally turned into space. The curly-brace code block need a semi-colon before the closing curly-brace when on the same line, which declare -f funcname does not provide since it expands the function source with closing curly brace at a new line.

    Now lets make this behaviour very straightforward:

    $ sudo bash -c 'echo hello 
    echo world'
    hello
    world
    

    It executes both echo statements because they are separated by a newline.

    but:

    $ sudo -i bash -c 'echo hello 
    echo world'
    hello echo world
    

    It executes the first echo statement that prints everything as arguments because the newline has been replaced by a space.