使用Sudo执行Bash脚本功能


22

我有一个脚本,可以执行许多不同的操作,其中大多数不需要任何特殊特权。但是,我包含在一个函数中的一个特定部分需要root特权。

我不希望整个脚本都以root身份运行,我希望能够以root权限从脚本中调用此函数。如有必要,提示输入密码不是问题,因为无论如何它都是交互式的。但是,当我尝试使用时sudo functionx,我得到:

sudo: functionx: command not found

如我所料,export没什么不同。我希望能够直接在脚本中执行该功能,而不是出于多种原因而将其分解并作为单独的脚本执行。

有什么办法可以使我的函数对sudo可见,而无需将其解压缩,找到合适的目录,然后将其作为独立脚本执行?

该函数本身大约一页长,包含多个字符串,其中一些用双引号引起来,而某些用单引号引起来。它还取决于主脚本中其他位置定义的菜单功能。

我只希望拥有sudo ANY的人能够运行该功能,因为它的功能之一就是更改密码。


涉及多个功能的事实使它变得更加复杂并且容易出现故障。现在,您必须找到所有此类依赖项(以及它们的所有依赖项,如果有的话……到很多深层次),包括菜单函数可能调用的所有其他函数以及declare它们。
cas

同意,如果没有更好的选择,我可能不得不硬着头皮将其分解(并尽我所能准确地确定其运行路径,并希望最终用户将文件保存在一起)。
BryKKan '16

Answers:


19

我会承认,没有简单,直观的方法可以做到这一点,这有点令人讨厌。但是,您可以这样做:

function hello()
{
    echo "Hello!"
}

# Test that it works.
hello

FUNC=$(declare -f hello)
sudo bash -c "$FUNC; hello"

或更简单地说:

sudo bash -c "$(declare -f hello); hello"

这个对我有用:

$ bash --version
GNU bash, version 4.3.42(1)-release (x86_64-apple-darwin14.5.0)
$ hello
Hello!
$
$ FUNC=$(declare -f hello)
$ sudo bash -c "$FUNC; hello"
Hello!

基本上,declare -f将返回函数的内容,然后将其传递给bash -c内联。

如果要从bash的外部实例导出所有函数,请更改FUNC=$(declare -f hello)FUNC=$(declare -f)

编辑

要解决有关报价的注释,请参见以下示例:

$ hello()
> {
> echo "This 'is a' test."
> }
$ declare -f hello
hello ()
{
    echo "This 'is a' test."
}
$ FUNC=$(declare -f hello)
$ sudo bash -c "$FUNC; hello"
Password:
This 'is a' test.

1
echo "Hello!"实际上是偶然的,因为它实际上是相同的echo Hello!(即双引号对此特定的echo命令没有区别)。在许多/大多数其他情况下,函数中的双引号可能会破坏bash -c命令。
cas

1
这确实回答了原始问题,所以如果我没有更好的解决方案,我会接受。但是,它确实破坏了我的特定功能(请参阅我的编辑),因为它依赖于脚本中其他地方定义的功能。
BryKKan

1
我做了一些测试今天下午早些时候(使用bash -xc而不是仅仅bash -c),它看起来像bash很聪明足以重新报价的事情在这种情况下,甚至有单引号代替双引号和改变的程度',以'\''在必要时。我确定在某些情况下它无法处理,但是它肯定至少对简单和中等复杂的情况有效-例如,尝试function hello() { filename="this is a 'filename' with single quotes and spaces" ; echo "$filename" ; } ; FUNC=$(declare -f hello) ; bash -xc "$FUNC ; hello"
cas

4
@cas declare -f以可以由bash重新解析的方式打印出函数定义,因此bash -c "$(declare -f)" 可以正常工作(假设外壳也是bash)。您发布的示例显示它可以正常工作-引号已更改在trace中,因为bash使用shell语法打印出迹线,例如trybash -xc 'echo "hello world"'
Gilles'SO- stop

3
极好的答案。我实现了您的解决方案-我会注意到,您可以从脚本内部导入脚本本身,前提是您将其嵌套在有条件的sudo yourFunction
容器中

5

“问题”是sudo清除环境(除了少数允许的变量之外),并将一些变量设置为预定义的安全值,以防范安全风险。换句话说,这实际上不是问题。这是一个功能。

例如,如果您将PATH 设置为PATH="/path/to/myevildirectory:$PATH"并且sudo未将PATH设置为预定义值,则任何未指定其运行的ALL命令的完整路径名的脚本(即大多数脚本)都将在/path/to/myevildirectory其他目录之前查找。在其中放置诸如lsgrep或其他常用工具的命令,您就可以轻松地在系统上执行任何操作。

最简单/最佳的方法是将函数重新编写为脚本,然后将其保存在路径中(或在sudo命令行上指定脚本的完整路径-除非sudo配置为允许您仍然需要执行此操作)以root身份运行ANY命令),并使其可执行chmod +x /path/to/scriptname.sh

重写一个shell功能的脚本很简单,只要刚刚保存函数定义中的命令到一个文件中(不function ...{}线)。


这不会以任何方式回答问题。他特别希望避免将其放入脚本中。
2016年

1
sudo -E避免清理环境。
2016年

我在某种程度上了解为什么会这样。我希望有一些方法可以暂时替代此行为。提到了-E选项,尽管在这种情况下不起作用。不幸的是,尽管我很欣赏如何使它成为一个独立脚本的解释,但是它并不能解决这个问题,因为我想要一种避免这种情况的方法。我无法控制最终用户将脚本放置在何处,我想避免使用硬编码的目录,也避免使用试图准确确定主脚本从何处运行的方式。
BryKKan

这是否是OP所要求的。如果他想要的东西要么行不通,要么只能通过做一些极不安全的事情才能使之工作,那么就需要告知他们并提供替代方案-即使替代方案是他们明确表示自己不想要的(因为有时,这是安全地执行此操作的唯一或最佳方法)。告诉某人如何用脚射击自己而不用警告他们将枪对准他们的脚并扣动扳机可能产生的后果是不负责任的。
cas

1
@cas是的。在某些情况下,不能安全地完成此操作是可以接受的答案。看到我的最后编辑。我很想知道您是否对安全性有相同的看法。
BryKKan'3

3

我已经编写了自己的Sudobash函数来执行此操作,它可以调用函数和别名:

function Sudo {
        local firstArg=$1
        if [ $(type -t $firstArg) = function ]
        then
                shift && command sudo bash -c "$(declare -f $firstArg);$firstArg $*"
        elif [ $(type -t $firstArg) = alias ]
        then
                alias sudo='\sudo '
                eval "sudo $@"
        else
                command sudo "$@"
        fi
}

2

您可以结合使用函数和别名

例:

function hello_fn() {
    echo "Hello!" 
}

alias hello='bash -c "$(declare -f hello_fn); hello_fn"' 
alias sudo='sudo '

然后sudo hello工作


1

这是威尔答案的一种变体。它涉及一个附加cat过程,但提供了Heredoc的舒适性。简而言之,它是这样的:

f () 
{
    echo ok;
}

cat <<EOS | sudo bash
$(declare -f f)
f
EOS

如果您想多想些事情,请尝试以下操作:

#!/bin/bash

f () 
{ 
    x="a b"; 
    menu "$x"; 
    y="difficult thing"; 
    echo "a $y to parse"; 
}

menu () 
{
    [ "$1" == "a b" ] && 
    echo "here's the menu"; 
}

cat <<EOS | sudo bash
$(declare -f f)
$(declare -f menu)
f
EOS

输出为:

here's the menu
a difficult thing to pass

在这里,我们获得了menu与问题中的函数相对应的函数,该函数“在主脚本的其他位置定义”。如果“别处”表示sudo在执行功能需求时已在此阶段读取了其定义,则情况类似。但是它可能尚未被阅读。可能还有另一个函数将触发其定义。在这种情况下declare -f menu,必须替换为更复杂的内容,或者以如下方式更正整个脚本:menu已经声明函数。


很有意思。我必须在某个时候尝试一下。是的,该menu功能将在此之前声明,就像f从菜单中调用的那样。
BryKKan

0

假设您的脚本是(a)自包含的,或者(b)可以根据脚本的位置来获取其组件(而不是记住主目录的位置),则可以执行以下操作:

  • 使用$0脚本的路径名(在sudo命令中使用该路径名),并传递一个脚本将检查的选项,以调用密码更新。只要您依靠在路径中查找脚本(而不是仅仅运行)./myscript),就应该在中获得绝对路径名$0
  • 由于sudo运行脚本,因此可以访问脚本中所需的功能。
  • 在脚本的顶部(而不是在函数声明之后),脚本将对其进行检查uid并意识到它是以root用户身份运行的,并且看到它具有设置为告诉它更新密码的选项,然后执行那。

脚本可能由于各种原因而自行重复出现:更改特权就是其中之一。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.