![如何引用 $@ 而不在 bash 函数中传递它?](https://linux22.com/image/226678/%E5%A6%82%E4%BD%95%E5%BC%95%E7%94%A8%20%24%40%20%E8%80%8C%E4%B8%8D%E5%9C%A8%20bash%20%E5%87%BD%E6%95%B0%E4%B8%AD%E4%BC%A0%E9%80%92%E5%AE%83%EF%BC%9F.png)
我想知道如何$@
从另一个函数或另一个脚本文件引用 ,而不需要像所做的那样传递它getopts
。非常感谢!
# 上一个问题: bash getopts 如何知道调用有哪些参数
A() {
B
}
B() {
# how to reference the $@ of A here ?
# which will be 1 2 3 4
}
A 1 2 3 4
答案1
如果不以某种方式A
提供其位置参数,您就无法做到这一点。B
可以通过传递它们:
A() {
B "$@"
}
B() {
[ "$#" -eq 0 ] || printf '<%s>\n' "$@"
}
或者将它们存储在数组中:
A() {
A_args=( "$@" )
B
}
B() {
[ "${#A_args[@]}" -eq 0 ] || printf '<%s>\n' "${A_args[@]}"
}
由于 bash 与 ash/ksh88/zsh(但与 ksh93 或 zsh 的private
变量不同)对其局部变量进行动态作用域,您甚至可以将该A_args
变量设置local
为A
,并且它仍然可以在调用的函数中使用A
。
bash
让您访问变量其父范围,即使它们是阴影下的通过利用其unset
内置函数的当前错误(在某些情况下不会取消设置,而是显示父作用域中的变量),在当前作用域中使用局部变量,但您无法通过这种方式从父作用域访问位置参数,因为在 bash 中(与 // 相反zsh
) ,位置参数无法映射到数组变量,因为 bash 数组设计是根据 Korn shell 设计的rc
,fish
其中数组并不是真正的数组。
如果您希望某些代码能够访问或修改(通过shift
或set
)函数的位置参数或本地选项,或者return
从函数中访问或修改,则需要该代码在该函数的范围内运行,以便不能位于单独的函数中。
您可以使用别名(它们是 C 预处理器宏的 shell 等效项,但它们不能接受参数)或eval
不过。
但请注意:
- 在 bash shell 中以及不在 POSIX 模式下时,默认情况下会禁用别名扩展,因此您需要
shopt -s expand_aliases
. - 别名必须存在前读取函数的代码,并在函数的代码内部进行扩展。
所以你可以这样做:
[ -z "$BASH_VERSION" ] || shopt -s expand_aliases # work around for bash
alias B='{
[ "$#" -eq 0 || printf "<%s>\n" "$@"
shift
}'
A() {
echo "$#"
B
echo "$#"
}
A a b c
或者:
B='
[ "$#" -eq 0 || printf "<%s>\n" "$@"
shift
'
A() {
echo "$#"
eval "$B"
echo "$#"
}
A a b c
答案2
实际上,在 bash 中,当extdebug
启用该选项(通常用于调试器)时,调用堆栈中所有函数的位置参数列表将在包含参数数量的$BASH_ARGV
特殊数组中可用(尽管顺序相反)$BASH_ARGC
在每个级别。
但请注意:
- 这些是只读的(好吧,尝试改变它们的值没有效果)
- extdebug 必须从一开始就设置(在调用第一个函数之前),但不能在 bash 命令行 (
) 上设置,因为它会调用调试器。bash -O extdebug
- 检查手册以了解启用该选项的其他影响。
shopt -s extdebug
shopt -u xpg_echo
A() {
B x y
}
reverse_helper() { caller_args=( "${BASH_ARGV[@]:0:BASH_ARGC}" ); }
B() {
local -a caller_args
local IFS=,
reverse_helper "${BASH_ARGV[@]:BASH_ARGC[0]:BASH_ARGC[1]}"
echo "my args: $*"
echo "my caller args: ${caller_args[*]}"
}
A a b c
这使:
my args: x,y
my caller args: a,b,c
答案3
根据评论,OP 询问如何在函数中模仿内置函数的行为,例如shift
和。getopts
实际上可以通过一些技巧来实现,尽管我不确定在生产中使用它是一个好主意。
A() {
B
}
B() {
trap 'shift;trap debug' debug
}
A 1 2 3 4
trap
必须是函数 B 中的最后一个命令。
如果您想在返回后保留原始调试陷阱:
A() {
B
}
B() {
local olddebug="$(trap -p debug)"
trap "shift;trap debug;$olddebug" debug
}
A 1 2 3 4
与别名相比,函数可以在定义之前在另一个函数中引用,递归调用,并重新定义并在同一函数中调用新版本。如果将脚本文件中的所有内容(包括别名)包装到一个大函数中并调用该函数,别名将会失败,尽管它在生产中也很少使用。