2012-02-25 69 views
23

我尝试导出函数,然后使用bash执行它,但不工作:如何使用sudo执行bash函数?

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

如果我尝试没有sudo使用bash运行功能(bash的-c“my_func,并将”),它的工作原理。

有什么想法?

+0

为什么你需要运行这样的说法? – 2012-02-25 23:20:47

+0

因为整个脚本通过标准输入传送到bash。它可能来自卷曲或猫.. – Miroslav 2012-02-26 01:33:16

+0

SF上的同一个问题:http://serverfault.com/questions/177699/how-can-i-execute-a-bash-function-with-sudo/ – 2012-03-31 08:59:48

回答

13

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=("[email protected]")    ## 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=("[email protected]") 
    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打电话
    Hello yourname!
    您通过以下PARAMS:
    第一
    第二

  2. 调用使用sudo
    您好根!
    您通过以下PARAMS:
    -n
    约翰·多恩
    -s



如果你需要使用这个在shell调用的函数,而在你的bashrc中定义,就像另一个用户在serverfault上提出类似的问题一样,那么你必须把以前的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! 
+2

这是一项相当大的工作!做得好。顺便说一句,只有我修改的是sudo调用部分。我添加了退出代码传播,所以我可以在测试工具中使用漂亮的小函数。这是我的修改: 从这: $ sudo的庆典 “$ TMPFILE” $ RM “$ TMPFILE” 这样: $ sudo的庆典 “$ TMPFILE” $ EXIT_CODE = $? $ rm“$ tmpfile” $ return $ EXIT_CODE – LavaScornedOven 2013-10-16 15:13:23

+0

这种感觉很神奇:/ – sleepycal 2014-11-02 14:20:58

+1

难道你不能用'{echo'#!/ bin/bash'生成临时文件吗?声明-f“$ 1”|头-n -1 | tail -n +3); }>“$ tmpfile”',而不是将参数打包到脚本中,而是将它们作为sudo的一部分传递给它?它会将身体缩小至5条线 – jbo5112 2017-05-18 18:31:19

12

每次运行sudo时,它都会分叉并执行shell的新副本,并以root用户身份运行。该shell不会从你的shell继承函数(它不能),也不会继承以前执行的函数。你将不得不写出一个包含函数定义和调用的文件,并请求调用该文件。

+1

不仅它不能,它可能永远不会那样做,由于安全原因。 – 2012-02-25 23:14:07

+0

Runnig bash本身也会执行bash的新副本,但它仍然能够从父shell中继承函数。 另外,sudo在某种程度上能够保留导出的bash变量,所以我认为可能会有一些技巧对函数做同样的事情。 据我所知,这可能有一些安全原因,但如果我使用sudo在更受限制的用户下运行功能会怎么样? – Miroslav 2012-02-26 01:27:49

+0

变量遍历过程环境机制。功能没有这样的事情。 – bmargulies 2012-02-26 01:56:41

1

简而言之,简单不具有在其单引号的东西,这对我的作品:

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

whoAmI 
' 
sudo bash -c "$code" 

# output: root 
9

你可以做,使用declare -f,如下面的例子:

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

ttt dd 
DECL=`declare -f ttt` 

sudo bash -c "$DECL; ttt asd" 
3

与调用你的函数替代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!"; 
} 

函数命中sudo并要求输入密码。 (然后管道“y”到ipfw的提示,不涉及这个问题)。之后,sudo被缓存,所以该函数的其余部分无需输入密码即可执行。

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

portforward 8000 
Password: 
Forwarding localhost to port 8000! 

,填补我的需要,因为我只需要输入一次密码,它采取的照顾。虽然如果第一次输入密码失败,会有点难看。用于检测第一个sudo是否成功的额外点以及否则退出该函数。

0

所有你需要做的是检查,如果你是根,如果是,运行功能,如果不是,则调用使用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