How to referencing $@ without pass it in bash function?

I wants to know how can I reference the $@ from another function or another script
file without pass it just like what getopts do. Thank you very much!

# previous question :
How bash getopts get to know what arguments the call has

A() {
  B 
} 

B() {

  # how to reference the $@ of A here ?
  # which will be 1 2 3 4


}

A 1 2 3 4
Asked By: Till

||

You can’t without A making its positional parameters available to B one way or another. Could be by passing them along:

A() {
  B "$@"
}
B() {
  [ "$#" -eq 0 ] || printf '<%s>n' "$@"
}

Or storing them in an array:

A() {
  A_args=( "$@" )
  B
}
B() {
  [ "${#A_args[@]}" -eq 0 ] || printf '<%s>n' "${A_args[@]}"
}

As bash, like ash/ksh88/zsh (but unlike ksh93 or zsh’s private variables) does dynamic scoping for its local variables, you could even make that A_args variable local to A and it would still be available in the functions that A calls.

bash lets you access variables of its parent scope even when they’re shadowed by local variables in the current scope by exploiting a current bug of its unset builtin (which under some conditions doesn’t unset but reveal the variable from the parent scope), but you can’t access the positional parameters from the parent scope that way because in bash (contrary to zsh/rc/fish…), positional parameters cannot be mapped to array variables as bash array design is shaped after that of the Korn shell where arrays are not really arrays.

If you want some code to be able to access or modify (via shift or set) the function’s positional parameters or local options, or return from the function, you need that code to run in the scope of that function, so that cannot be in a separate function.

You could use aliases (which are the shell equivalent of C pre-processor macros, except they can’t take arguments) or eval though.

Beware though that:

  • in the bash shell and when not in POSIX mode, alias expansion is disabled by default so you’ll need shopt -s expand_aliases.
  • the alias must exists before the code of the function is read, and it is expanded inside the code of the function.

So you could do:

[ -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

Or:

B='
  [ "$#" -eq 0 || printf "<%s>n" "$@"
  shift
'
A() {
  echo "$#"
  eval "$B"
  echo "$#"
}
A a b c
Answered By: Stéphane Chazelas

Actually, in bash, when the extdebug option is enabled (normally used for the debugger), the list of positional parameters of all the functions in the call stack is made available in the $BASH_ARGV special array (though in reverse order) with $BASH_ARGC containing the number of arguments at each level.

Beware though that:

  • those are readonly (well, trying to change their value has no effect)
  • the extdebug must be set from the start (before the first function is invoked), but not on the bash command line (bash -O extdebug) as that invokes the debugger instead.
  • check the manual for the other implications of enabling that option.
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

Which gives:

my args: x,y
my caller args: a,b,c
Answered By: Stéphane Chazelas

Based on the comments, OP is asking about how to imitate the behaviors of builtins such as shift and getopts in a function. It’s actually possible with some tricks, though I’m not sure it’s a good idea to use it in production.

A() {
  B
} 

B() {
  trap 'shift;trap debug' debug
}

A 1 2 3 4

trap must be the last command in the function B.

If you want to preserve the original debug trap after return:

A() {
  B
} 

B() {
  local olddebug="$(trap -p debug)"
  trap "shift;trap debug;$olddebug" debug
}

A 1 2 3 4

Compared to aliases, functions could be referenced in another function before defined, called recursively, and redefined and get the new version called in the same function. Aliases would fail if you wrap everything in a script file including the aliases into a big function and call the function, though it’s also rarely used in production.

Answered By: user23013
Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.