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
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
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 (
) as that invokes the debugger instead.bash -O extdebug
- 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
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.