2016-11-12 54 views
0

我正在学习bash。我想创建一个函数,将另一个函数包含在时间脚本文件中,并在子shell中使用sudo -u命令执行它。 我遇到的问题是生成的脚本找不到包装函数,尽管它是在包装函数中导出的。 我在下面添加测试线。有人发现问题,请让我知道。非常感谢你。如何在bash中的函数中导出函数?

main.sh

source "./display.sh" 
source "./sudo_wrap.sh" 

display_func "load success" 

sudo_wrap_func '' 'display_func' '3' '4 5' 

输出,display.sh,sudo_wrap.sh和生成的时间文件如下所附,

输出

display_func : load success 
export -f display_func 
30481: line 5: display_func: command not found 

display.sh

function display_func() { 
    echo "display_func : [email protected]" 
} 

sudo_wrap.sh

function sudo_wrap_func() { 

    local sudo_user="${1:-root}" 
    local function_name="${2:?'function_name is null string.'}" 
    shift 2 
    local func_augs=("[email protected]") 

    local script 

    # *** script : header *** 
    script="#!/bin/bash\n" 
    script="${script}\n" 

    # *** script : making augments for function *** 
    script="${script}augs=(" 

    for aug in "${func_augs[@]}" 
    do 
    if [[ "${aug}" =~ [[:blank:]] ]]; then 
     script=" ${script} \"${aug}\"" 
    else 
     script=" ${script} ${aug}" 
    fi 
    done 
    script="${script})\n" 

    local tmp_script_file="${RANDOM}" 

    echo -e "${script}" >> "${tmp_script_file}" 

    # *** script : calling function with augments *** 

    echo -e "${function_name} \"\${augs[@]}\"\n" >> "${tmp_script_file}" 

    echo "export -f "${function_name}"" >&2 
    export -f "${function_name}" 

    sudo -u"${sudo_user}" bash "${tmp_script_file}" 
    rm "${tmp_script_file}" 

} 

临时生成的文件(在这种情况下,文件名是30481)

#!/bin/bash 

augs=(3 "4 5") 

display_func "${augs[@]}" 
+2

看起来相当复杂。你究竟在努力实现什么? – Robert

+0

检查'tempfile'命令以防止临时竞争。 – Robert

+0

@罗伯特:谢谢你的评论。我想要做的是用sudo命令运行一个函数。我无法运行sudo some_function。所以我把这个函数包装在另一个脚本中,并用sudo命令运行它。顺便说一下,'温度赛'是什么?非常感谢你。 – mora

回答

2

正如我在评论说,基本的问题是,sudo清除它的环境(包括变量和函数)在作为另一个用户运行命令(/脚本)之前。这可以被sudo -E覆盖,但只有它在/ etc/sudoers中被明确允许才可以被覆盖

但问题不解决;您只需将该函数的定义包含在脚本中,以便在该环境中重新创建该函数。 bash甚至有一个方便的命令declare -f display_func,它以适当的形式打印函数定义(并且declare -p variable对变量也是如此)。所以你可以使用这些来为脚本添加适当的定义。

这是我写的一个脚本来做到这一点。我对您的脚本做了其他一些更改:我以-u username指定不同的用户运行(因此,如果您不想指定其他用户,则不必将''作为第一个参数传递)。我还添加了-f functionname-v variablename将附加函数和变量定义“导出”到脚本中(以防主函数依赖于它们)。我还在/ tmp中创建临时脚本文件,并在必要时更改所有权,以便其他用户可读。

#!/bin/bash 

me="$(basename "$0")" 
usage() { 
    echo "Usage: $me [-u user] [-f otherfunction] [-v variablename] function [args...]" >&2 
} 

tmp_script_file=$(mktemp "/tmp/${me}.XXXXXXXXXXXX") || { 
    echo "Error creating temporary script file" >&2 
    exit 1 
} 
echo "#!/bin/bash" > "$tmp_script_file" # Not actually needed, since we'll run it with "bash" 

# Parse the command options; "-u" gets stored for later, but "-f" and "-v" write 
# the relevant declarations to the script file as we go. 
sudo_user="" 
while getopts u:f:v: OPT; do 
    case "$OPT" in 
     u) 
     sudo_user="$OPTARG" ;; 

     f) 
     declare -f "$OPTARG" >>"$tmp_script_file" || { 
      echo "Error saving definition of function $OPTARG" >&2 
      exit 1 
     } ;; 

     v) 
     declare -p "$OPTARG" >>"$tmp_script_file" || { 
      echo "Error saving definition of variable $OPTARG" >&2 
      exit 1 
     } ;; 

     ?) usage; exit 1 ;; 
    esac 
done 
shift $(($OPTIND-1)) 

if (($# == 0)); then # No actual command specified 
    usage 
    exit 1 
fi 

# Write the main function itself into the script 
declare -f "$1" >>"$tmp_script_file" || { 
    echo "Error saving definition of function $1" >&2 
    exit 1 
} 

# Then the command to run it, with arguments quoted/escaped as 
# necessary. 
printf "%q " "[email protected]" >>"$tmp_script_file" 
# the printf above won't write a newline, so add it by hand 
echo >>"$tmp_script_file" 

# If the script will run as someone other than root, change ownership of the 
# script so the target user can read it 
if [[ -n "$sudo_user" ]]; then 
    sudo chown "$sudo_user" "$tmp_script_file" 
fi 

# Now launch the script, suitably sudo'ed 
sudo ${sudo_user:+ -u "$sudo_user"} bash "$tmp_script_file" 

# Clean up 
sudo rm "$tmp_script_file" 

下面是使用它的一个例子:

$ foo() { echo "foo_variable is '$foo_variable'"; } 
$ bar() { echo "Running the function bar as $(whoami)"; echo "Arguments: $*"; foo; } 
$ export -f foo bar # need to export these so the script can see them 
$ export foo_variable='Whee!!!' # ditto 
$ # Run the function directly first, so see what it does 
$ bar 1 2 3 
Running the function bar as gordon 
Arguments: 1 2 3 
foo_variable is 'Whee!!!' 
$ # Now run it as another user with the wrapper script 
$ ./sudo_wrap.sh -f foo -v foo_variable -u deenovo bar 1 2 3 
Running the function bar as deenovo 
Arguments: 1 2 3 
foo_variable is 'Whee!!!' 

需要注意的是,你可以删除需要通过运行该脚本source或使其成为一个功能导出函数和变量,但这样做将需要更改$me的定义方式,usage函数,用returns取代所有那些exit,也许还有其他一些我没有想到的东西。

+0

很酷。这是什么用例?具体来说,你为什么需要动态生成一个脚本?为什么不直接调用现有脚本(带或不带sudo)? – Robert

+1

@Robert:脚本很容易通过sudo运行; shell函数不是。我不知道具体的用例是什么,但这是不可能直接做到的。 –

+0

@Robert:如果你可以自己修改函数,最好把sudo放在函数里面,而不要像上面提到的chepner那样创建另一个在子shell中运行的脚本。顺便说一句,我是一名初学者程序员。所以我从编程设计的角度来看并不适用于其他人。 – mora