accepted answer 运行良好,但 脚本使用 sudo 按需重新调用自身的惯用语 可以简化 并使更便携:
[[ $(id -u) -eq 0 ]] || exec sudo /bin/bash -c "$(printf '%q ' "$BASH_SOURCE" "$@")"
使用 [[ ... ]] 代替 [ ... ] 使得在操作数之前(或双引号 LHS)之前添加 x 是不必要的。
-
使用bash -c 而不是su -c 来解释重构的命令行使命令更便携,因为并非所有平台都支持su -c(例如,macOS 不支持)。
李>
在bash中,$BASH_SOURCE通常是引用运行脚本的更可靠的方式。
使用上述方法,参数中的任何变量引用或命令/算术替换总是由 调用 shell 扩展。
如果您想要 延迟 扩展 - 这样变量引用不会在 sudo shell 运行之前在 root 的上下文中扩展 用户 - 使用这个:
(( __reinvoked )) || exec sudo -s __reinvoked=1 "$BASH_SOURCE" "$@"
请注意,您必须单个引用包含变量引用或命令替换的任何参数,以便它们被延迟扩展;例如,'$USER'。
注意使用 ad-hoc 环境变量 __reinvoked 以确保重新调用恰好一次(即使最初已经以 root 用户身份调用)。
这是一个演示第一种技术的示例脚本:
#!/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
acfreitas's helpful answer 演示了一种“script-inside-a-script”技术,其中here-document 用于通过stdin 向sudo su 提供shell 代码。
同样,sudo -s 就足够了并且引用很重要:
sudo -s -H <<'EOF'
echo "$HOME"
EOF
请注意这里文档的开头分隔符 EOF 在这种情况下是如何引用的,以防止文档的内容被预先解释当前 shell。
如果您没有引用(任何部分)EOF,$HOME 将扩展为 当前 用户的主目录。
如果您想混合预先扩展和延迟扩展,请保留此处开头的文档分隔符不加引号并选择性地\-引用$ 实例:
sudo -s -H <<EOF
echo "Called by: $USER; root's home dir: \$HOME"
EOF