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"
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.