6

I need to read and write the positional parameters $@ of a function's caller. The Bash man page says that:

A shell function is an object that is called like a simple command and executes a compound command with a new set of positional parameters

So $@ is rewritten at every call. I looked for some "special parameter" but found nothing. The shell variable BASH_ARGV seems to solve my problem, however it requires shopt -s extdebug enabled, what isn't the default behavior in my machine neither looks like a option to turn on in production.

extdebug
    If set, behavior intended for use by debuggers is enabled:
    ...
    4.     BASH_ARGC and BASH_ARGV are updated as described in their
           descriptions above.
    ...

Is Bash capable of read or write a function's caller $@ without BASH_ARGV? Do you think that Bash is limited and use another shell for scripting?

Edit: I want a fancy getopt wrapper inside my helper library. So all behavior related to it goes inside a function. No need to check errors or set --.

2 Answers2

2

A function cannot affect its caller's positional parameters. This is by design: positional parameters are meant to be private to the function.

Make your function work on an array.

myfunction () {
  local _myfunction_arrayname=$1
  shift
  … # work on the positional parameters
  eval "$_myfunction_arrayname=(\"\$@\")"
}
myfunction foo "$@"
set -- "${foo[@]}"

In ksh93 and bash, there's a roundabout way to do something approaching by combining an alias and the . (source) builtin with a process substitution. Example.

alias myfunction='. <(echo myfunction_body \"\$@\"; echo set -- "\"\${new_positional_parameters[@]}\"")'

Put the meat of the work of the function in myfunction_body and make it set the array new_positional_parameters. After a call to myfunction, the positional parameters are set to the values that myfunction_body puts in new_positional_parameters.

0

Here's some code which loops through the parameters, updating each of them as necessary. In this case, replacing duplicate slashes with a single one:

for param
do
    param="$(printf %s. "$1" | tr -s "/")"
    set -- "$@" "${param%.}"
    shift
done
l0b0
  • 51,350
  • Off topic question, but why not set -- "${@//\/+(\/)//}" instead of all that? (Note that requires shopt -s extglob, which is off by default for non-interactive shells.) – manatwork Oct 17 '12 at 10:06
  • @manatwork Nice one-liner for this specific situation. It's just a (verbose) demonstration of how to do arbitrary modification to the parameters. – l0b0 Oct 17 '12 at 10:46