Bash 更新呼叫者';函数中的参数数组

Bash 更新呼叫者';函数中的参数数组,bash,function,command-line-arguments,Bash,Function,Command Line Arguments,在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中,我可以通过以下方式设置
$@

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
如何使函数更新调用方的
$@
?我试过了,但没用

我试图编写一个函数来处理命令行参数并从中删除某些项(同时设置一个变量),这样调用者就不必担心它们了。例如,如果我使用
--debug
调用脚本,我希望所有脚本都打开调试日志记录,而不必编写代码来处理每个脚本中的日志记录,并将该逻辑放在公共的“源代码”函数中


注意:我不想派生子shell。

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

您所能做的最好的事情就是传递要处理的参数,然后返回 那些还没有处理的

类似于:

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
如果您不想遇到“子shell”的麻烦,可以使用全局变量:

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=()

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--$(f)
这样的黑客一般不会工作,因为它不能处理参数中的空白

一个通用的解决方案变得更加丑陋:您需要在函数中
printf'%s\0'$parameter'
,在脚本中
而IFS=read-r-d'-u9
将返回的值放入数组,然后
设置--“${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 "$@"

这里的一个实施示例:

我认为您的第二个建议是最好的;但是
参数
应该是一个数组。(可能使用不同的名称。)如果将
参数
用作数组,则将执行
设置--${arguments}
,风险是仅获取第一个数组项作为新的位置参数。Re:“如果将参数用作数组,则将执行
设置--${arguments}
”:你到底为什么要那样做???我错了,@ruakh。我已经根据你的建议更新了答案。