我无法确定这个问题出在哪里。当我在终端运行它并输入密码时,什么也没有发生,但是如果我在终端中分别运行每个命令,它就可以正常工作。谢谢!
#!/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
我无法确定这个问题出在哪里。当我在终端运行它并输入密码时,什么也没有发生,但是如果我在终端中分别运行每个命令,它就可以正常工作。谢谢!
#!/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
<<
是一条指令,用于读取输入,直到找到包含指定定界符的行,如EOF
(文件结尾)。sudo su <<EOF
echo "code"
EOF
例如
#!/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
命令sudo su
可以启动一个交互式的root shell,但它不会将当前shell转换为root shell。
要做你想做的事情的惯用语是这样的(感谢@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
该想法是检查当前用户是否为root,如果不是,则使用su
重新运行相同的命令。
"$0 $@"
这种写法是不正确的,因为它试图将 $@
(一个数组)压缩成一个字符串,这会在空格、通配符等方面出现严重问题。更好的写法是 printf -v cmd_str '%q ' "$0" "$@"; exec sudo su -c "$cmd_str"
。 - Charles Duffy[ x$foo = xbar ]
这种写法在任何符合 POSIX.2 标准的 shell 中都是不必要的 -- 它是 Bourne 时代的遗留物 -- 但如果你想要一些与正确性相关的偏执症,你应该真正地加上引号:[ "$UID" != 0 ]
;否则,即使有了 x
,如果你扩展后的字符串分成多个单词,你仍然会得到语法错误... 即使对于 $UID
,如果你的 IFS
包含数字字符,这种情况也可能发生。 - Charles Duffyx
吗?如果你不是在引用(毕竟,在这里你没有引用),你会发现空字符串的情况下会出现错误...但是只添加 x
只能修复空字符串的情况,而添加引号则可以修复空字符串和多个单词的情况。 - Charles Duffyprintf %q
负责发出一个字符串,该字符串可以安全地按原样评估。 - Charles Duffybash
的功能,并用sudo -s
替换了sudo su -c
,这个解决方案可以简化为[[ $(id -u) -eq 0 ]] || exec sudo -s "$BASH_SOURCE" $(printf '%q ' "$@")
。 - mklement0sudo su
不是在shell内运行的命令--它会启动一个新的shell。
这个新的shell已经不再运行你的脚本了,而原来正在运行脚本的旧的shell会等待新的shell退出才会继续执行。
sudo
和su
通常支持直接在TTY上提示。 - Charles Duffy被接受的答案很好,但是使用sudo
重新调用脚本的习语可以简化并使其更加通用:
[[ $(id -u) -eq 0 ]] || exec sudo /bin/bash -c "$(printf '%q ' "$BASH_SOURCE" "$@")"
使用[[ ... ]]
代替[ ... ]
使得不需要在操作数前添加x
(或在左操作数加双引号)。
使用bash -c
代替su -c
来解释重构后的命令行使得命令更加便携,因为并非所有平台都支持su -c
(例如macOS不支持)。
在bash
中,$BASH_SOURCE
通常是更可靠的方式来指向正在运行的脚本。
使用上述方法,参数中的任何变量引用或命令/算术替换都会被调用shell自动扩展。
如果你反而想要延迟扩展——这样变量引用直到sudo
shell以root用户的上下文运行才扩展——请使用以下方式:
(( __reinvoked )) || exec sudo -s __reinvoked=1 "$BASH_SOURCE" "$@"
'$USER'
. __reinvoked
确保重新调用一次(即使最初已作为root用户调用)。
如果未作为root调用脚本,则使用sudo -s
重新调用自身,并将所有参数原样传递。
除非先前经过身份验证并仍处于超时期间,否则 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
acfreitas的有用回答演示了一种“脚本内嵌脚本”的技术,其中使用here-document通过stdin提供shell代码给sudo su
。
同样,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
sudo su会尝试作为root启动一个新的shell。 一旦打开了这个新的shell,原始脚本将不会继续执行,直到新的shell被关闭。
要修复此问题,请尝试:
在shell脚本中尝试:
su <username> -c "my command"
su userA -c "mkdir /opt/D3GO/"
然而,如果你是用户A并且想要以root身份运行脚本的一部分,系统会提示你输入密码。
su root -c "mkdir /opt/D3GO/"
你也可以通过在一开始就使用sudo运行脚本来解决这个问题。
sudo ./myScript.sh
这样脚本就会保留原始用户,您可以使用标准变量(如${USERNAME}、${UID}等)来访问它。
取决于哪种方式对您更有效。
su root
命令也为我打开了密码提示。 - Don Cheadle运行“sudo su”会打开一个新的shell并且该命令直到您退出该shell后才返回。也许可以将脚本拆分成两个文件:第一个运行sudo并在sudo下执行第二个脚本。
"$PWD"
比$(pwd)
或其反引号等效形式更加高效;每次运行命令替换时,都会fork()
出一个子进程,在该单独的进程中运行给定的命令(在本例中为pwd
),然后通过管道读取其输出;而$PWD
直接在父shell中计算。 - Charles Duffy