如何在 bash 中提升权限到 root 并返回?

如何在 bash 中提升权限到 root 并返回?

如何提升权限到 root bash?然后如何恢复到之前的状态?我的问题是关于提升/降低权限以及某些应用程序(尤其是 GUI 应用程序(和“环境”))可能出现的麻烦。

比如,有些应用程序为每个用户和 root 保留单独的配置文件。如果我以 root 身份运行应用程序,GUI 和配置文件可能不是“环境”的唯一问题,无论这是它们或操作系统的错误还是功能。

比如说,我需要以 root 身份运行一个脚本,或者已经以 root 身份运行了一个脚本(比如在 rc.local 中),所以你可能需要降低权限或从那里“完全”切换回普通用户(su normaluser并不总是有效)。首先运行一个脚本,sudo -H然后切换到另一个用户?这并不总是按需要工作,尤其是对于 GUI 应用程序。我相信问题出在“环境”上,并且或DISPLAY=:0 ...DISPLAY=:0 gtk-launch ...可能gtk-launch有错误)可能还没有帮助。

假设我gedit打开了一个文档。如果我以普通用户身份运行gedit /doc2,它会打开菜单可见在已经存在的窗口中另一个标签。如果我以 root 身份运行它,它会在一个单独的窗口中打开,并且没有可见的菜单。如果我通过sudo -H或运行脚本su并尝试以普通用户身份在那里运行gedit /doc2(再次使用 sudo (-H) 或 su),那么它会像我以 root 身份而不是普通用户身份执行一样工作。我也尝试了sudo选项-l, -s, -i。对于其他 GUI 应用程序,这导致了更严重的问题。一些 GUI 应用程序具有不同的 GUI 或根本无法运行。有时runuser对我有帮助,但并非总是如此。并且 heredoc 格式(sudo -H /bin/bash <<EOF <lines with commands> EOF)不能按预期工作。

这会带来很多麻烦。一年多来,我一直找不到一个好的通用解决方案。那么有什么方法可以解决海拔权限并返回?或者其他好的解决方法?

为了以防万一,下面是一个完整的示例(以sudo ./script.sh或 使用运行-H):

cd /somedir
some_commands # using the current directory, root privileges and setting some variables, and writing to somefile (better as normal user)
sudo -u normaluser /bin/bash -c 'gedit --encoding someencoding /somefile'`

如果我运行以下 bash 脚本./script.sh

gedit --encoding someencoding /somefile

然后就gedit可以正常工作了。

以防万一:它与 Ubunu 16.04 xenial、bash 版本 4.3.48 有关。

更新

我知道我可以运行类似的命令

sudo sh -c ‘command1 $somevariable; command2’

或者(我发现它可以是几行)

sudo sh -c ‘command1 $somevariable command2’

或者可能与 类似bash。这可能不适用于大量命令,并且不能解决所有问题。而且我绝对不需要以交互方式输入命令。另请参阅我的回答。

PS 我认为Linux应该是用户友好的并且易于使用。

答案1

要提升脚本中几个命令的权限,请使用带有 heredoc 语法的 sudo:

possiblevariable=something    
sudo /bin/bash <<EOF
    cd /somedir
    pwd
    commandasroot1 "$possiblevariable"
    commandasroot2
EOF
nonrootcommand (and not in /somedir)

测试 cd:(工作目录在 heredoc 中发生改变,但在 heredoc 末尾恢复为之前的状态)

leonid@DevSSD:~$ sudo bash <<EOF
> cd /tmp
> pwd
> EOF
[sudo] password for leonid: 
/tmp
leonid@DevSSD:~$ 

再举一个例子,说明变量替换在 heredoc 中是如何工作的:

leonid@DevSSD:~$ sudo bash <<EOF
    cd /tmp
    echo $PWD; echo \$PWD
EOF
[sudo] password for leonid: 
/home/leonid
/tmp
leonid@DevSSD:~$ 

更新:例如如何将输出放入变量

leonid@DevSSD:~$ variable=$(sudo bash <<EOF
    cd /tmp
    echo $PWD; echo \$PWD
EOF
)
[sudo] password for leonid: 
leonid@DevSSD:~$ echo $variable
/home/leonid /tmp

答案2

我没有很多脚本可以从用户权限提升到sudo(超级用户)权限。讽刺的是,由于您的问题与gedit我拥有的一个名为 的脚本有关sgedit,因此创建了它,因为gksu gedit不再受支持,并且因为 root 用户无法设置选项卡设置、字体首选项等。

#!/bin/bash

# NAME: sgedit
# PATH: /mnt/e/bin
# DESC: Run gedit as sudo using $USER preferences
# DATE: June 17, 2018.

# Must not prefix with sudo when calling script
if [[ $(id -u) == 0 ]]; then
    zenity --error --text "You cannot call this script using sudo. Aborting."
    exit 99
fi

# Get user preferences before elevating to sudo
gsettings list-recursively | grep -i gedit | grep -v history | \
    grep -v docinfo | \
    grep -v virtual-root | grep -v state.window > /tmp/gedit.gsettings

sudoFunc () {

    # Must be running as sudo
    if [[ $(id -u) != 0 ]]; then
        zenity --error --text "Sudo password authentication failed. Aborting."
        exit 99
    fi

    # Get sudo's gedit preferences
    gsettings list-recursively | grep -i gedit | grep -v history | \
        grep -v docinfo | \
        grep -v virtual-root | grep -v state.window > /tmp/gedit.gsettings.root
    diff /tmp/gedit.gsettings.root /tmp/gedit.gsettings | grep '>' > /tmp/gedit.gsettings.diff
    sed -i 's/>/gsettings set/g; s/uint32 //g' /tmp/gedit.gsettings.diff
    chmod +x /tmp/gedit.gsettings.diff
    bash -x /tmp/gedit.gsettings.diff  # Display override setting to terminal
#    nohup gedit $@ &>/dev/null &
    nohup gedit -g 1300x840+1+1220 $@ &>/dev/null &
#              Set the X geometry window size (WIDTHxHEIGHT+X+Y).

}

FUNC=$(declare -f sudoFunc)
sudo -H bash -c "$FUNC; sudoFunc $*;"

exit 0

必须在常规用户模式下调用该脚本。它将从用户配置文件复制gsettingsgedit/tmp复制字体大小、换行、制表符设置、将制表符转换为空格和插件等重要设置。

然后sudo请求密码并将状态提升为 root。

sudo -H用于相当于gksu保护,防止root非法权限破坏用户配置文件。

的下一个根配置设置gedit将从复制到的调用用户的配置文件中继承/tmp

gedit作为后台任务加载,并向用户显示sudo已打开文件的版本。例如/etc/default/grub

权限sudo立即被删除,命令行提示符返回。但是,gedit它仍然在单独的窗口中运行,并且文件已打开以供编辑。

答案3

我的答案是:然而,需要一个解决方案,即如何提升和降低权限(实际上,从概念上讲,甚至不需要包装,单独存在)或完全切换到 root 并返回。

根据我、LeonidMew 和 WinEunuuchs2Unix 的建议,以下是解决方法。这个-H选项通常是一个不错的选择,所以我将其包括在内。

有两种解决方法(bash可以替换为sh):

1)sudo -H /bin/bash -c ‘commands’

2) sudo -H /bin/bash -c “commands”(以及 heredoc 格式,sudo -H /bin/bash <<EOF <lines with commands> EOF)。

1) 外部变量不会自动在内部可见。必须将它们移入内部、进行双重定义或作为参数传递。我无法像$(declare -f functionname)在内部那样创建或传递外部函数的简短(重新)声明(也许这仍然可行),但如果我只是将其移入内部,它就可以正常工作。

2) 仅将外部变量的副本传递到内部。您必须使用\构造$(...)$PWD其他本地定义的变量进行转义,并且使用注释#可能不起作用(与使用一样#$(declare -f ...))。像这样的参数$1是整个脚本的参数,不能作为局部变量传递到内部。外部定义的函数在内部不可见,但可以在内部重新声明,例如$(declare -f functionname)

在这两种情况下,您都可以通过文件获取输出,或者通过包装从 stdout (或几个空格分隔的变量) 获取快速字符串输出res=$(...)。您可以在此处查看示例:https://stackoverflow.com/a/23567255。虽然在包装之后,所有 EOL 都转换为空格。也许export,旨在将变量传递给子进程,将有助于避免包装或文件的使用,但不知何故它对我来说不起作用。

情况 1) 似乎是最佳默认选择,并且很可能需要对现有代码进行较少的修改,而情况 2) 通常需要对现有代码进行仔细的修改。也许您会发现这两种情况同时有用。

这是我的简单示例:

tmpdir=/tmp/ tmpfile=/tmp/tmpfile res=$(sudo -H bash -c 'tmpdir=$1; tmpfile=$2 echo "$tmpdir;" cd $ tmpdir echo $(pwd) echo "A string from file" > $tmpfile ' - $tmpdir $tmpfile) echo $res arr=($res) echo ${arr[0]} # Hellooo echo ${arr[1]} # world; echo ${arr[*]} # whole array echo ${#arr[*]} # number of items n=0 echo ${#arr[$n]} # length of the n-th element cat $tmpfile sudo sh -c 'rm -f $2' - - $tmpfile

相关内容