我正在学习bash。我想创建一个函数,它将另一个函数包装在一个临时脚本文件中,并在子shell中使用sudo -u命令执行它。 我遇到的问题是生成的脚本找不到包装函数,尽管它是在wrap函数中导出的。 我在下面附上测试线。有人发现问题,请告诉我。非常感谢你。
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 : $@"
}
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=( "$@" )
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[@]}"
答案 0 :(得分:3)
正如我在评论中所说,基本问题是sudo
在以另一个用户身份运行命令(/ script)之前清除其环境(包括变量和函数)。这可以用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 " "$@" >>"$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
功能,将所有exit
替换为returns
,以及其他一些我没有想过的事情。