如何使用sudo执行bash函数?


29

我在全局bashrc中定义了一个bash函数,该函数需要root特权才能工作。如何使用sudo运行它,例如sudo myfunction。默认情况下,它给出一个错误:

sudo:myfunction:找不到命令


2
没试过,但这个博客帖子似乎处理:w00tbl0g.blogspot.com/2007/05/...
Grizly

上述脚本的安装需要'set alias sudo = sudowrap',这不是可取的imho。请参阅我的答案以获取不需要任何操作的解决方案。
卡·博里昂

这是shell函数有害的许多原因之一。Shell函数应限于要更改环境的命令。其余使用实际的脚本。函数的优点是什么?(好吧,“过度使用”是邪恶的,不是功能本身。我敢打赌,还有许多其他好的理由。但是编写脚本时,它们应该是例外,而不是规则。)
杰夫·里尔曼

Answers:


4

卢卡(Luca)友善地向我提出了这个问题,这是我的方法:在调用sudo之前扩展函数/别名,并将其全部传递给sudo,不需要临时文件。

在我的博客上解释。有很多报价处理:-)

# Wrap sudo to handle aliases and functions
# Wout.Mertens@gmail.com
#
# Accepts -x as well as regular sudo options: this expands variables as you not root
#
# Comments and improvements welcome
#
# Installing: source this from your .bashrc and set alias sudo=sudowrap
#  You can also wrap it in a script that changes your terminal color, like so:
#  function setclr() {
#   local t=0               
#   SetTerminalStyle $1                
#   shift
#   "$@"
#   t=$?
#   SetTerminalStyle default
#   return $t
#  }
#  alias sudo="setclr sudo sudowrap"
#  If SetTerminalStyle is a program that interfaces with your terminal to set its
#  color.

# Note: This script only handles one layer of aliases/functions.

# If you prefer to call this function sudo, uncomment the following
# line which will make sure it can be called that
#typeset -f sudo >/dev/null && unset sudo

sudowrap () 
{
    local c="" t="" parse=""
    local -a opt
    #parse sudo args
    OPTIND=1
    i=0
    while getopts xVhlLvkKsHPSb:p:c:a:u: t; do
        if [ "$t" = x ]; then
            parse=true
        else
            opt[$i]="-$t"
            let i++
            if [ "$OPTARG" ]; then
                opt[$i]="$OPTARG"
                let i++
            fi
        fi
    done
    shift $(( $OPTIND - 1 ))
    if [ $# -ge 1 ]; then
        c="$1";
        shift;
        case $(type -t "$c") in 
        "")
            echo No such command "$c"
            return 127
            ;;
        alias)
            c="$(type "$c")"
            # Strip "... is aliased to `...'"
            c="${c#*\`}"
            c="${c%\'}"
            ;;
        function)
            c="$(type "$c")"
            # Strip first line
            c="${c#* is a function}"
            c="$c;\"$c\""
            ;;
        *)
            c="\"$c\""
            ;;
        esac
        if [ -n "$parse" ]; then
            # Quote the rest once, so it gets processed by bash.
            # Done this way so variables can get expanded.
            while [ -n "$1" ]; do
                c="$c \"$1\""
                shift
            done
        else
            # Otherwise, quote the arguments. The echo gets an extra
            # space to prevent echo from parsing arguments like -n
            while [ -n "$1" ]; do
                t="${1//\'/\'\\\'\'}"
                c="$c '$t'"
                shift
            done
        fi
        echo sudo "${opt[@]}" -- bash -xvc \""$c"\" >&2
        command sudo "${opt[@]}" bash -xvc "$c"
    else
        echo sudo "${opt[@]}" >&2
        command sudo "${opt[@]}"
    fi
}
# Allow sudowrap to be used in subshells
export -f sudowrap

这种方法的一个缺点是它只会扩展您正在调用的函数,而不会扩展您从那里引用的任何其他函数。如果您引用的是bashrc中加载​​的函数(前提是在bash -c调用中执行),则Kyle的方法可能会更好地处理该问题。


在ServerFault上,最好显示所需的代码并链接到外部站点,这样用户不必单击即可获得所需的信息,从而使这些信息在外部站点潜在的死亡中幸存下来。
出色的编译器

15

您可以使用export函数将其提供给bash -c要在其中使用的子Shell或脚本。

your_function () { echo 'Hello, World'; }
export -f your_function
bash -c 'your_function'

编辑

这适用于直接子shell,但显然sudo不转发函数(仅转发变量)。甚至使用setenvenv_keep和的各种组合env_reset似乎也无济于事。

编辑2

然而,似乎su 支持导出的函数。

your_function () { echo 'Hello, World'; }
export -f your_function
su -c 'your_function'

2
+1,我会说这是正确的答案。
凯尔·勃兰特

1
这种方法是否有效?就我而言不是。
pradeepchhetri 2012年

@pradeepchhetri您可能想提供更多信息,例如您正在尝试什么,使用哪个shell以及使用哪个OS。
Legolas 2012年

@Legolas:我正在尝试上面脚本中编写的相同脚本。我得到了错误bash: your_function: command not found。我正在使用Ubuntu 11.04bash shell
pradeepchhetri 2012年

@pradeepchhetri如果使用,该sudo -E bash -c 'your_function'怎么办?
Legolas 2012年

4

也许你可以做:

function meh() {
    sudo -v
    sudo cat /etc/shadow
}

这应该可以工作,并且可以避免在命令行上键入sudo。


1
根据您的系统...这将在每次调用sudo命令时提示您输入密码...或提示一次并对其进行缓存。最好检测一下您是否以root用户身份运行,如果不是,则...用sudo再次调用bash脚本。
TheCompWiz 2010年

我还没有遇到过一个不会缓存sudo密码的系统:timestamp_timeout的默认值为5。如果将其设置为0,则总是要求输入密码,但这将是一个自定义设置。
wzzrd 2010年

3

如果您需要在sudo上下文中调用函数,请使用declare

#!/bin/bash

function hello() {
  echo "Hello, $USER"
}

sudo su another_user -c "$(declare -f hello); hello"

只要您的函数不调用其他函数,此方法就起作用。
modiX

对于我的用例,这是最佳答案。
吉姆(Jim)

2

我将通过让sudo执行外壳本身来执行新外壳,然后该函数将以root特权运行。例如:

vim myFunction
#The following three lines go in myFunction file
function mywho {
    sudo whoami
}

sudo bash -c '. /home/kbrandt/myFunction; mywho'
root

您甚至还可以为该sudo bash行创建别名。


2
#!/bin/bash

function smth() {
    echo "{{"
    whoami
    echo "}}"
}

if [ $(whoami) != "root" ]; then
    whoami
    echo "i'm not root"
    sudo $0
else
    smth
fi

2

正如Legolas在对Dennis Williamson的回答的评论中指出的那样,您应该阅读关于stackoverflow上类似问题的bmargulies的回答。

从那开始,我写了一个函数来解决这个问题,基本上实现了bmargulies的想法。

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
# EXESUDO
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
#
# Purpose:
# -------------------------------------------------------------------- #
# Execute a function with sudo
#
# Params:
# -------------------------------------------------------------------- #
# $1:   string: name of the function to be executed with sudo
#
# Usage:
# -------------------------------------------------------------------- #
# exesudo "funcname" followed by any param
#
# -------------------------------------------------------------------- #
# Created 01 September 2012              Last Modified 02 September 2012

function exesudo ()
{
    ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
    #
    # LOCAL VARIABLES:
    #
    ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##

    #
    # I use underscores to remember it's been passed
    local _funcname_="$1"

    local params=( "$@" )               ## array containing all params passed here
    local tmpfile="/dev/shm/$RANDOM"    ## temporary file
    local filecontent                   ## content of the temporary file
    local regex                         ## regular expression
    local func                          ## function source


    ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
    #
    # MAIN CODE:
    #
    ### ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##

    #
    # WORKING ON PARAMS:
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    #
    # Shift the first param (which is the name of the function)
    unset params[0]              ## remove first element
    # params=( "${params[@]}" )     ## repack array


    #
    # WORKING ON THE TEMPORARY FILE:
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    content="#!/bin/bash\n\n"

    #
    # Write the params array
    content="${content}params=(\n"

    regex="\s+"
    for param in "${params[@]}"
    do
        if [[ "$param" =~ $regex ]]
            then
                content="${content}\t\"${param}\"\n"
            else
                content="${content}\t${param}\n"
        fi
    done

    content="$content)\n"
    echo -e "$content" > "$tmpfile"

    #
    # Append the function source
    echo "#$( type "$_funcname_" )" >> "$tmpfile"

    #
    # Append the call to the function
    echo -e "\n$_funcname_ \"\${params[@]}\"\n" >> "$tmpfile"


    #
    # DONE: EXECUTE THE TEMPORARY FILE WITH SUDO
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    sudo bash "$tmpfile"
    rm "$tmpfile"
}



用法示例:
运行以下代码段

#!/bin/bash

function exesudo ()
{
    # copy here the previous exesudo function !!!
}

test_it_out ()
{
    local params=( "$@" )
    echo "Hello "$( whoami )"!"
    echo "You passed the following params:"
    printf "%s\n" "${params[@]}" ## print array
}

echo "1: calling without sudo"
test_it_out "first" "second"

echo ""
echo "2. calling with sudo"
exesudo test_it_out -n "john done" -s "done"

exit



将输出

  1. 不用sudo打电话
    你好!
    您通过了以下参数:
    第一

  2. 用sudo
    呼叫Hello root!
    您通过了以下参数:
    -n
    john done
    -s
    foo



如果您需要在shell中使用它(如您要求的那样)来调用在bashrc中定义的函数,那么您还必须将先前的exesudo函数也放置在同一bashrc文件中,如下所示:

function yourfunc ()
{
echo "Hello "$( whoami )"!"
}
export -f yourfunc

function exesudo ()
{
   # copy here
}
export -f exesudo



然后,您必须注销并再次登录或使用

source ~/.bashrc



最后,您可以按照以下方式使用exesudo:

$ yourfunc
Hello yourname!

$ exesudo yourfunc
Hello root!

它返回/dev/shm/22481: No such file or directory
modiX
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.