Bash – Circular name references in bash shell function, but not in ksh

bashfunctionkshtypesetvariable

I'm writing a set of shell functions that I want to have working in both Bash and KornShell93, but with Bash I'm running into a "circular name reference" warning.

This is the essence of the problem:

function set_it {
    typeset -n var="$1"

    var="hello:$var"
}

function call_it {
    typeset -n var="$1"

    set_it var
}

something="boff"
call_it something
echo "$something"

Running it:

$ ksh script.sh
hello:boff

$ bash script.sh
script.sh: line 4: warning: var: circular name reference
hello:

KornShell93 does exactly what I want, but Bash fails, and also warns about the same thing on line 2 if the something variable in the script is named var instead.

I'd like to have the var variable be local to each function, which is why I use typeset, but Bash doesn't seem to like "dereferencing" a nameref to a variable with the same name as the nameref itself. I can't use local -n or declare -n since it would break in ksh which lacks these, and even if I did, it doesn't solve the issue.

The only solution I've found is to use unique variable names in each function, which seems rather silly since they are local.

The Bash manual says the following about typeset:

typeset […]

-n Give each name the nameref attribute, making it a name
reference to another variable. That other variable is
defined by the value of name. All references and
assignments to name, except for changing the -n
attribute itself, are performed on the variable referenced by name's value.

[…]

When used in a function, declare and typeset make each name
local, as with the local command, unless the -g option is
supplied. If a variable name is followed by =value, the
value of the variable is set to value.

It is obvious that there is something I don't understand about Bash's name references and function-local variables.

So, the question is: In this case, am I missing something about Bash's handling of name reference variables, or is this a bug/mis-feature in Bash?

Update: I'm currently working with GNU bash, version 4.3.39(1)-release (x86_64-apple-darwin15) as well as with GNU bash, version 4.3.46(1)-release (x86_64-unknown-openbsd6.0). The Bash shipped with macOS is too old to know about name references at all.

Update: Even shorter:

function bug {
    typeset -n var="$1"
    printf "%s\n" "$var"
}

var="hello"
bug var

Results in bash: warning: var: circular name reference. The var in the function should have different scope from the var in the global scope. This imposes an unnecessary restriction on the caller. The restriction being "you're not allowed to name your variables whatever you want, because there may be a name clash with a (local) nameref in this function".

Best Answer

Chet Ramey (Bash maintainer) says

There was extensive discussion about namerefs on bug-bash earlier this year. I have a reasonable suggestion about how to change this behavior, and I will be looking at it after bash-4.4 is released.

In the meanwhile, I'm resorting to slightly obfuscate the names of my local nameref variables, so that they don't clash within the library nor (hopefully) with global shell variable names.


In bash 5.0, this is ever so slightly remedied (but not really fixed). The following is the observed behaviour:

$ foo () { typeset -n var="$1"; echo "$var"; }
$ var=hello
$ foo var
bash: typeset: warning: var: circular name reference
bash: warning: var: circular name reference
bash: warning: var: circular name reference
hello

This shows that it works, but that there also are are a few warnings.

The relevant NEWS entry says

i. A nameref name resolution loop in a function now resolves to a variable by
that name in the global scope.