2016-11-12 49 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