如何使用sudo执行bash函数?

时间:2021-03-07 15:06:41

I tried exporting the function and then executing it with bash, but that doesn't work:

我尝试导出该函数,然后使用bash执行它,但这不起作用:

$ export -f my_func
$ sudo bash -c 'my_func' 
bash: my_func: command not found

If I try to run the function with bash without sudo (bash -c 'my_func'), it works.

如果我尝试使用没有sudo的bash运行该函数(bash -c'my_func'),它就可以了。

Any idea?

任何想法?

6 个解决方案

#1


14  

Starting from the answer of bmargulies, I wrote a function to cover this issue, which basically realizes his idea.

从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"
}



Example of usage:
running the following snippet

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

#!/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



Will output

会输出

  1. calling without sudo
    Hello yourname!
    You passed the following params:
    first
    second

    没有sudo的电话你好你的名字!你通过了下面的参数:第一秒

  2. calling with sudo
    Hello root!
    You passed the following params:
    -n
    john done
    -s
    foo

    用sudo调用Hello root!你通过了下面的参数:-n john done -s foo



If you need to use this in a shell calling a function which is defined in your bashrc, as asked with a similar question on serverfault by another user, then you have to put the previous exesudo function on the same bashrc file as well, like the following:

如果你需要在一个shell中使用它来调用你的bashrc中定义的函数,就像另一个用户在serverfault上提出的类似问题一样,那么你必须将之前的exesudo函数放在同一个bashrc文件中,比如以下:

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

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



Then you have to logout and login again or use

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

source ~/.bashrc



Finally you can use exesudo as follow:

最后你可以使用exesudo如下:

$ yourfunc
Hello yourname!

$ exesudo yourfunc
Hello root!

#2


15  

Each time you run sudo, it forks and execs a new copy of the shell, running as root. That shell does not inherit functions from your shell (it can't) and it doesn't inherit functions from previous executions. You will have to write out a file containing the function definition and invocation and sudo the invocation of that.

每次运行sudo时,它都会以root身份运行并执行shell的新副本。该shell不会从shell继承函数(它不能)并且它不会继承先前执行的函数。您将必须写出包含函数定义和调用的文件,并对其进行调用。

#3


10  

You can do that using declare -f, as in the following example:

您可以使用declare -f执行此操作,如以下示例所示:

function myfunc() {
    whoami
    echo First parameter is $1
}

myfunc foo
DECL=`declare -f myfunc`

sudo bash -c "$DECL; myfunc bar"

#4


3  

An alternative to calling your function with sudo is to just move the "sudo " calls inside your function. For example, I was wanting to set up a shortcut function in OS X for forwarding localhost to a particular port.

使用sudo调用函数的另一种方法是在函数内部移动“sudo”调用。例如,我想在OS X中设置一个快捷方式函数,用于将localhost转发到特定端口。

function portforward() {
    echo "y" | sudo ipfw flush;
    sudo ipfw add 100 fwd 127.0.0.1,$1 tcp from any to any 80 in;
    echo "Forwarding localhost to port $1!";
}

The function hits sudo and asks for my password. (Then pipes "y" to a prompt for ipfw, not related to this question). After that, sudo is cached so the rest of the function executes without the need to enter a password.

该功能命中sudo并询问我的密码。 (然后将“y”管道输入到ipfw的提示符,与此问题无关)。之后,缓存sudo,以便执行其余功能而无需输入密码。

Essentially, this just runs like:

从本质上讲,这只是运行:

portforward 8000
Password:
Forwarding localhost to port 8000!

And fills my need because I only need to enter the password once and it's taken care of. Although it's a little ugly if you fail to enter the password the first time. Extra points for detecting whether the first sudo succeeded and exiting the function if not.

并满足我的需求,因为我只需要输入一次密码就可以了。虽然如果你第一次输入密码失败会有点难看。用于检测第一个sudo是否成功以及是否退出该函数的额外点。

#5


1  

For short and simple stuff that doesn't have single quotes in it, this works for me:

对于没有单引号的简短而简单的东西,这对我有用:

export code='
function whoAmI() {
    echo `whoami`
}

whoAmI
'
sudo bash -c "$code"

# output: root

#6


0  

All you have to do is check if you are root, if yes, run the function, if not, call the script with sudo:

您所要做的就是检查您是否是root用户,如果是,则运行该函数,如果没有,请使用sudo调用脚本:

#!/bin/bash
# script name: 'foo.sh'

function foo(){
    whoami
}

DIR=$( cd "$( dirname "$0" )" && pwd )   # get script dir
if [ "$UID" -ne 0 ]; then                # check if you are root
    sudo $DIR/foo.sh                     # NOT: run script with sudo
else
    foo                                  # YES: run function
fi

#1


14  

Starting from the answer of bmargulies, I wrote a function to cover this issue, which basically realizes his idea.

从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"
}



Example of usage:
running the following snippet

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

#!/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



Will output

会输出

  1. calling without sudo
    Hello yourname!
    You passed the following params:
    first
    second

    没有sudo的电话你好你的名字!你通过了下面的参数:第一秒

  2. calling with sudo
    Hello root!
    You passed the following params:
    -n
    john done
    -s
    foo

    用sudo调用Hello root!你通过了下面的参数:-n john done -s foo



If you need to use this in a shell calling a function which is defined in your bashrc, as asked with a similar question on serverfault by another user, then you have to put the previous exesudo function on the same bashrc file as well, like the following:

如果你需要在一个shell中使用它来调用你的bashrc中定义的函数,就像另一个用户在serverfault上提出的类似问题一样,那么你必须将之前的exesudo函数放在同一个bashrc文件中,比如以下:

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

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



Then you have to logout and login again or use

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

source ~/.bashrc



Finally you can use exesudo as follow:

最后你可以使用exesudo如下:

$ yourfunc
Hello yourname!

$ exesudo yourfunc
Hello root!

#2


15  

Each time you run sudo, it forks and execs a new copy of the shell, running as root. That shell does not inherit functions from your shell (it can't) and it doesn't inherit functions from previous executions. You will have to write out a file containing the function definition and invocation and sudo the invocation of that.

每次运行sudo时,它都会以root身份运行并执行shell的新副本。该shell不会从shell继承函数(它不能)并且它不会继承先前执行的函数。您将必须写出包含函数定义和调用的文件,并对其进行调用。

#3


10  

You can do that using declare -f, as in the following example:

您可以使用declare -f执行此操作,如以下示例所示:

function myfunc() {
    whoami
    echo First parameter is $1
}

myfunc foo
DECL=`declare -f myfunc`

sudo bash -c "$DECL; myfunc bar"

#4


3  

An alternative to calling your function with sudo is to just move the "sudo " calls inside your function. For example, I was wanting to set up a shortcut function in OS X for forwarding localhost to a particular port.

使用sudo调用函数的另一种方法是在函数内部移动“sudo”调用。例如,我想在OS X中设置一个快捷方式函数,用于将localhost转发到特定端口。

function portforward() {
    echo "y" | sudo ipfw flush;
    sudo ipfw add 100 fwd 127.0.0.1,$1 tcp from any to any 80 in;
    echo "Forwarding localhost to port $1!";
}

The function hits sudo and asks for my password. (Then pipes "y" to a prompt for ipfw, not related to this question). After that, sudo is cached so the rest of the function executes without the need to enter a password.

该功能命中sudo并询问我的密码。 (然后将“y”管道输入到ipfw的提示符,与此问题无关)。之后,缓存sudo,以便执行其余功能而无需输入密码。

Essentially, this just runs like:

从本质上讲,这只是运行:

portforward 8000
Password:
Forwarding localhost to port 8000!

And fills my need because I only need to enter the password once and it's taken care of. Although it's a little ugly if you fail to enter the password the first time. Extra points for detecting whether the first sudo succeeded and exiting the function if not.

并满足我的需求,因为我只需要输入一次密码就可以了。虽然如果你第一次输入密码失败会有点难看。用于检测第一个sudo是否成功以及是否退出该函数的额外点。

#5


1  

For short and simple stuff that doesn't have single quotes in it, this works for me:

对于没有单引号的简短而简单的东西,这对我有用:

export code='
function whoAmI() {
    echo `whoami`
}

whoAmI
'
sudo bash -c "$code"

# output: root

#6


0  

All you have to do is check if you are root, if yes, run the function, if not, call the script with sudo:

您所要做的就是检查您是否是root用户,如果是,则运行该函数,如果没有,请使用sudo调用脚本:

#!/bin/bash
# script name: 'foo.sh'

function foo(){
    whoami
}

DIR=$( cd "$( dirname "$0" )" && pwd )   # get script dir
if [ "$UID" -ne 0 ]; then                # check if you are root
    sudo $DIR/foo.sh                     # NOT: run script with sudo
else
    foo                                  # YES: run function
fi