从函数内更新调用者的参数数组

Updating caller's argument array from within a function

在Bash中我可以这样设置$@

set -- a b c

然后,我可以检查 $@:

的内容
printf '%s\n' "$@"

这将显示:

a
b
c

但是,如果我在一个函数中这样做:

f() {
    set d e f
}

set a b c
f
printf '%s\n' "$@"

我仍然得到

a
b
c

而不是

d
e
f

如何让我的函数更新调用者的 $@?我试过 BASH_ARGV,但没用。

我正在尝试编写一个函数来处理命令行参数并从那里删除某些项目(同时设置变量),以便调用者不需要为它们操心。例如,如果我使用 --debug 调用它们,我希望我的所有脚本都打开它们的调试日志记录,而不必编写代码来处理每个脚本中的代码并将该逻辑放在一个通用的 "sourced" 函数中.

注意:我不想分叉子 shell。

您不能更改参数的值,因为它们是通过引用传递的 在 bash 个函数中。

你能做的最好的事情就是传递你想要处理的参数,return 还没有处理的。

行中的内容:

process_arguments() {
    # process the arguments
    echo "original arguments : $@"
    local new_arguments=(a c)

    echo ${new_arguments[@])
}

new_arguments=$(process_arguments a b c)
set -- $new_arguments

如果你不想"subshell"的麻烦,你可以使用全局变量:

arguments=""

process_arguments() {
    # process the arguments
    echo "original arguments : $@"
    local new_arguments=(a c)
    arguments="${new_arguments[@]}"
}

process_arguments a b c # no subshell
set -- $arguments        

根据@ruakh 的建议,您可以 使用arguments 作为数组,如下所示:

arguments=()

process_arguments() {
    # process the arguments
    echo "original arguments : $@"
    local new_arguments=(a c)
    arguments=( "${new_arguments[@]}" )
}

process_arguments a b c # no subshell
set -- "${arguments[@]}"

这是范围的问题:每个函数都有自己的参数数组,独立于脚本:

$ cat test.bash 
#!/usr/bin/env bash
f() {
    printf '%s\n' "Function arguments:" "$@"
}
printf '%s\n' "Script arguments:" "$@"
f 'a b' 'c d'
$ chmod u+x test.bash
$ ./test.bash 'foo bar' baz
Script arguments:
foo bar
baz
Function arguments:
a b
c d

所以当你set参数数组时,那只适用于当前范围。如果你想改变脚本参数数组,你需要set任何函数之外。像 set -- $(f) 这样的黑客一般不会起作用,因为它无法处理参数中的空格。

通用解决方案变得更加丑陋:您需要在函数中 printf '%s[=14=]' "$parameter" 并在脚本中 while IFS= read -r -d'' -u9 将返回值放入数组中,然后 set -- "${arguments[@]}" .

我希望这可以通过其他方式可靠地完成,但这就是我的全部。

方法很好,尽管该答案中的代码无法准确处理空格。

下面是基于相同想法的代码,可以很好地处理空格:

wrapper() {
  # filter args
  args=()
  for arg; do
    if [[ $arg = arg1 ]]; then
      # process arg1
    elif [[ $arg = arg2 ]]; then
      # process arg2
    elif ...
      # process other args
    else
      args+=("$arg")
    fi
  
    # call function with filtered args
    wrapped_function "$args[@]"
  done
}

wrapper "$@"

这里有一个示例实现:base-wrapper