如何引用 $@ 而不在 bash 函数中传递它?

如何引用 $@ 而不在 bash 函数中传递它?

我想知道如何$@从另一个函数或另一个脚本文件引用 ,而不需要像所做的那样传递它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变量设置localA,并且它仍然可以在调用的函数中使用A

bash让您访问变量其父范围,即使它们是阴影下的通过利用其unset内置函数的当前错误(在某些情况下不会取消设置,而是显示父作用域中的变量),在当前作用域中使用局部变量,但您无法通过这种方式从父作用域访问位置参数,因为在 bash 中(与 // 相反zsh) ,位置参数无法映射到数组变量,因为 bash 数组设计是根据 Korn shell 设计的rcfish其中数组并不是真正的数组。

如果您希望某些代码能够访问或修改(通过shiftset)函数的位置参数或本地选项,或者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

与别名相比,函数可以在定义之前在另一个函数中引用,递归调用,并重新定义并在同一函数中调用新版本。如果将脚本文件中的所有内容(包括别名)包装到一个大函数中并调用该函数,别名将会失败,尽管它在生产中也很少使用。

相关内容