更改Bash函数内的全局位置参数

时间:2015-03-25 14:33:32

标签: bash shell sh

有没有办法在函数中设置bash脚本的位置参数?

在全局范围内,可以使用set -- <arguments>来更改位置参数,但它在函数内部不起作用,因为它会更改函数的位置参数。

快速说明:

# file name: script.sh
# called as: ./script.sh -opt1 -opt2 arg1 arg2

function change_args() {
    set -- "a" "c" # this doesn't change the global args
}

echo "original args: $@" # original args: -opt1 -opt2 arg1 arg2
change_args
echo "changed args: $@" # changed args: -opt1 -opt2 arg1 arg2
# desired outcome:        changed args: a c

3 个答案:

答案 0 :(得分:1)

不是真的。函数实际上有自己的范围。默认情况下,函数中指定的参数是全局的,但如果您明确声明它,则它具有局部范围:

foo() {
    x=3 # global
    declare y # local
    ...
}

位置参数总是函数的局部参数,因此您操作它们所做的任何事情都只会在函数范围内生效。

技术上,您始终可以使用递归来解决此问题。如果您可以放心地再次调用原始脚本 ,则可以通过这种方式重新排序参数。 E.g。

declare -i depth

outer() {
    if (( depth++ < 1 )); then
        outer "${@:4:$#}" "${@:1:3}"
        return
    fi
    main "$@"
}

main() {
    printf '%s\n' "$@"
}

outer "$@"

对于我不理解的问题,这似乎是一个容易出错且容易混淆的解决方案,但它本质上是有效的。

答案 1 :(得分:1)

如前所述,答案是 no ,但如果有人需要这个,可以选择设置外部数组(_ARRAY),从函数内修改它,然后使用{事后{1}}。例如:

set -- ${_ARRAY[@]}

如果你测试它:

#!/bin/bash

_ARGS=()

shift_globally () {
  shift
  _ARGS=$@
}

echo "before: " "$@"
shift_globally "$@"
set -- "${_ARGS[@]}"
echo "after: " "$@"

从技术上讲,这不是你所要求的,但它是一种可以帮助需要类似行为的人的解决方法。

答案 2 :(得分:0)

我自己进行搜索,并提出以下内容,即通过将函数打印到stdout并用命令替换捕获它,从函数中返回一个由空格分隔的数组字符串(对不起,位置参数),然后解析该字符串逐字逐句地将其附加到位置参数中。像泥一样清除!

对于下面带有空格的args显然不起作用,但这当然也可以解决。

#standardSQL
select country, 
  count(distinct if(shared, id, null)) as Unique_overlapping_ids,
  count(distinct if(shared, null, id)) as Unique_non_overlapping_ids
from `project.dataset.table` 
join (
  select id, count(distinct country) > 1 shared
  from `project.dataset.table`
  group by id
) using(id)
group by country