如何在bash函数中导出函数?

时间:2016-11-12 15:52:05

标签: bash shell

我正在学习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[@]}" 

1 个答案:

答案 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,以及其他一些我没有想过的事情。