以 root 身份调用 bash 函数但给出 'unexpected end of file'

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

为什么最后一个示例会抛出错误,而其他示例却能正常工作? Bash 在任何情况下都会被调用。

#!/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"

由于 -i--login 开关而失败:

调试时好像是用-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'

现在如果手动执行它会导致同样的错误:

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

现在让我们做一个简单的小改动来让它工作:

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

原因是 sudo -i 像交互式 shell 一样运行所有内容。这样做时,declare -f hello 中的每个换行符都会在内部变成 space。大括号代码块在同一行时需要在结束大括号之前有一个分号,declare -f funcname 不提供,因为它在新行中用结束大括号扩展函数源。

现在让这个行为变得非常简单:

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

它执行两个 echo 语句,因为它们由换行符分隔。

但是:

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

它执行第一个 echo 语句,将所有内容打印为参数,因为换行符已被替换为 space。

It is the same code in all examples, so it should be ok.

是的,"$(declare -f hello); hello" 始终是相同的字符串。但是 sudo susudo -i 的处理方式不同,正如 发现的那样。

sudo -i 在将参数传递给 bash 之前引用它的参数。这个引用过程似乎没有记录并且非常糟糕。要查看实际执行的内容,您可以在 ~/.bash_profile/:

中打印传递给 bash -c 的参数

~/.bash_profile

的内容
cat <<EOF
# is executed as
$BASH_EXECUTION_STRING
# resulting in output
EOF

一些 sudo -i 糟糕且不一致的引用示例

换行符被换行符

$ sudo -u $USER -i echo '1
2'
# is executed as
echo 1\
2
# resulting in output
12

引号转义为文字

$ sudo -u $USER -i echo \'single\' \"double\"
# is executed as
echo \'single\' \"double\"
# resulting in output
'single' "double"

但是$没有被引用

$ sudo -u $USER -i echo $var
# is executed as
echo $var
# resulting in output


一些旁注:

您对su的用法可能有误解。

sudo su $USER bash -c "some command"

执行bash -c "echo 1; echo 2"-c ...su 解释并作为 -c ... 传递给 $USER 的默认值 shell。之后,剩余的参数也传递给 shell。执行的命令是

defaultShellOfUSER -c "some command" bash

你可能想写

sudo su -s bash -c "some command" "$USER"

交互式 shell 行为不同

su只是执行-c指定的命令。但是 sudo -i 开始登录 shell,在您的情况下,登录 shell 似乎是 bash(不一定是这种情况,请参见上一节)。

交互式 bash 会话的行为不同于 bash -c "..."bash script.sh。交互式 bash 源文件,如 .profile.bash_profile,并启用历史扩展、别名等。有关完整列表,请参阅 bash 手册中的 Interactive Shell Behavior 部分。