为什么我不能在shell脚本中使用'sudo su'?如何自动使用sudo运行shell脚本

Dus*_*vic 12 linux bash shell sudo

我无法弄清楚这有什么问题.当我在终端中运行并输入密码时,没有任何反应,但如果我在终端中单独运行每个命令,它就可以工作.谢谢!

#!/bin/bash    

sudo su;
mkdir /opt/D3GO/;
cp `pwd`/D3GO /opt/D3GO/;
cp `pwd`/D3GO.png /opt/D3GO/;
cp `pwd`/D3GO.desktop /usr/share/applications/;
chmod +x /opt/D3GO/D3GO
Run Code Online (Sandbox Code Playgroud)

rod*_*igo 15

Command sudo su启动一个交互式root shell,但它不会将当前shell转换为root.

做你想做的事的成语就像这样(感谢@CharlesDuffy的额外注意):

#check for root
UID=$(id -u)
if [ x$UID != x0 ] 
then
    #Beware of how you compose the command
    printf -v cmd_str '%q ' "$0" "$@"
    exec sudo su -c "$cmd_str"
fi

#I am root
mkdir /opt/D3GO/
#and the rest of your commands
Run Code Online (Sandbox Code Playgroud)

我们的想法是检查当前用户是否为root用户,如果没有,则重新运行相同的命令 su

  • `"$ 0 $ @"`是不对的 - 用空格,圆形字符等可怕地打破,因为它试图将`$ @`(一个数组)变成一个字符串.`printf -v cmd_str'%q'"$ 0""$ @"; exec sudo su -c"$ cmd_str"`会更接近. (2认同)
  • 做得很好; 充分利用`bash`特性并将`sudo su -c`替换为`sudo -s`,解决方案可以简化为`[[ $(id -u) -eq 0 ]] || exec sudo -s "$BASH_SOURCE" $(printf '%q ' "$@")` (2认同)

acf*_*tas 14

您可以使用Here Documents将输入重定向到交互式shell脚本.运算符<<是一个读取输入的指令,直到找到包含指定分隔符的行为EOF(文件末尾).

sudo su <<EOF
echo "code"
EOF
Run Code Online (Sandbox Code Playgroud)

例如

#!/bin/bash    
sudo su <<EOF
mkdir /opt/D3GO/
cp `pwd`/D3GO /opt/D3GO/
cp `pwd`/D3GO.png /opt/D3GO/
cp `pwd`/D3GO.desktop /usr/share/applications/
chmod +x /opt/D3GO/D3GO
EOF
Run Code Online (Sandbox Code Playgroud)


Cha*_*ffy 8

sudo su不是在shell中运行的命令 - 它启动一个新的shell.

这种新的外壳不再运行脚本,而且老贝运行脚本等待新的退出后再继续.


mkl*_*nt0 8

接受的答案效果很好,但对脚本成语重新调用本身sudo的需求可以简化并取得更便携:

[[ $(id -u) -eq 0 ]] || exec sudo /bin/bash -c "$(printf '%q ' "$BASH_SOURCE" "$@")"
Run Code Online (Sandbox Code Playgroud)
  • 使用[[ ... ]]而不是[ ... ]使x操作数前置(或双引用LHS)不必要.

  • 使用bash -c而不是su -c解释重构的命令行使命令更具可移植性,因为并非所有平台都支持su -c(例如,macOS不支持).

  • bash,$BASH_SOURCE通常是更可靠的方式来引用正在运行的脚本.


使用上述方法,参数中的任何变量引用或命令/算术替换总是由调用 shell 扩展.

如果您想要延迟扩展 - 以便在sudoshell运行之前不扩展变量引用,在root用户的上下文中- 使用:

(( __reinvoked )) || exec sudo -s __reinvoked=1 "$BASH_SOURCE" "$@"
Run Code Online (Sandbox Code Playgroud)

请注意,您必须引用包含变量引用或命令替换的任何参数,以便对它们进行延迟扩展; 例如,'$USER'.

请注意使用ad-hoc环境变量__reinvoked来确保重新调用一次(即使最初已经以root用户身份调用).


这是演示第一种技术的示例脚本:

  • 如果不以root身份调用,则脚本会重新调用自身sudo -s,并通过as-is传递所有参数.

  • 除非先前已经过身份验证且仍在超时期限内,sudo否则将提示输入管理员密码.

#!/bin/bash

[[ $(id -u) -eq 0 ]] || exec sudo /bin/bash -c "$(printf '%q ' "$BASH_SOURCE" "$@")"

# Print the username and all arguments.
echo "Running as: $(id -un)"
echo "Arguments:"
for arg; do echo "  $((++i)): [$arg]"; done
Run Code Online (Sandbox Code Playgroud)

acfreitas的有用答案演示了一种"脚本内部脚本"技术,其中here-document用于通过stdin提供shell代码sudo su.
再次,sudo -s足够,引用很重要:

sudo -s -H <<'EOF'
echo "$HOME"
EOF
Run Code Online (Sandbox Code Playgroud)

注意如何开此文档分隔符,EOF在这种情况下,报价以防止文档的内容从先期由解释当前的shell.
如果您没有引用(任何部分)EOF,$HOME将扩展到当前用户的主目录.

如果要混合前期扩展和延迟扩展,请将此处的开头 - 文档分隔符不加引号和选择性 - \引用$实例保留:

sudo -s -H <<EOF
echo "Called by: $USER; root's home dir: \$HOME"
EOF
Run Code Online (Sandbox Code Playgroud)