1

In bash I can use

local -n ref_name=$1 

to pass an argument to a function by reference, but the same syntax in ksh throws an error saying

typeset: -n: unknown option 

I tried nameref ref_name=$1 syntax as well but that also throws an error

nameref: cannot execute - No such file or directory 

ksh version

echo $KSH_VERSION @(#)PD KSH v5.2.14 99/07/13.2 

How do I achieve this reference functionality in KornShell?

3
  • 1
    Note that bash (and zsh in the next release) have copied namerefs from ksh about 3 decades later, but pdksh is not ksh, it's a no longer maintained (except in derivatives such as mksh or OpenBSD sh/ksh) part-clone of ksh88. Commented Aug 27 at 9:25
  • I rarely learn about "new" shells! Interesting day. Yeah, I can fully see how zsh might be too much for one's taste (and to bash-y from a ksh user POV), but isn't 99/07/13 the build date? I tried building pdksh-5.2.14 on my system, and it does seem to a lot of patching to even build with a C99 compiler on Linux. The configure misdetects the signal handler types (which defintely haven't changed, hm), the generation of a signal list (which should have been done at configure, not at compile time) falls on its face because, well, 2+ is not an integer) and so on. Maybe you want to use a … Commented Aug 27 at 10:17
  • … static build of mksh on that target system instead? I'd presume that'd be pretty backwards-compatible, but also not quite as stuck in the 90s… Commented Aug 27 at 10:18

2 Answers 2

1

Name references were added first to ksh93 in the initial release in late 1993.

They are essential there because ksh93 implements static variable scoping¹ (and to this day, it's still the only shell that does unless you want to consider zsh's private variables), as a function cannot access the local variables of its caller otherwise.

AFAIK, the first shell to copy the feature was mksh in 2009 (first in the R39b release). It is cruder there in that it doesn't attempt to make sure the nameref points to the variable of the caller, so you still need to namespace your local variables (including the nameref variable itself) to make sure the nameref doesn't end up referencing them (or itself recursively).

mksh, the shell of MirBSD and Android is based on the sh/ksh of OpenBSD, itself based on pdksh from the late 80s, itself based on the Forsyth shell (a clone of the Bourne shell) and intended as a public domain clone of ksh88¹. mksh has gradually been adding more and more features from ksh93 so contrary to pdksh it may no longer be seen as a ksh88 clone. Like ksh88, it does dynamic scoping like most shells, and I don't believe there's any plan to switch to static scoping.

bash (also with dynamic scoping) copied namerefs, with the same limitations as in mksh in 4.3 (2014).

zsh (also with dynamic scoping except for its private variables) added it first in 2023 along with a new zsh/ksh93 module with more features from ksh93, though as of writing it's still work in progress and there hasn't been a full release yet. It tries to be a bit more faithful to the ksh93 implementation, and avoids the variable clashing issues seen in mksh/bash in most cases.

To demonstrate the issues:

$ ksh93 -c 'function f { typeset -n a=$1; typeset b=2; a=3; }; b=1; f b; echo "$b"' 3 $ mksh -c 'function f { typeset -n a=$1; typeset b=2; a=3; }; b=1; f b; echo "$b"' 1 $ bash -c 'function f { typeset -n a=$1; typeset b=2; a=3; }; b=1; f b; echo "$b"' 1 $ zsh -c 'function f { typeset -n a=$1; typeset b=2; a=3; }; b=1; f b; echo "$b"' 3 
$ ksh93 -c 'function f { typeset -n a=$1; a=2; }; a=1; f a; echo "$a"' 2 $ mksh -c 'function f { typeset -n a=$1; a=2; }; a=1; f a; echo "$a"' E: f: typeset: a: expression recurses on parameter 2 $ bash -c 'function f { typeset -n a=$1; a=2; }; a=1; f a; echo "$a"' environment: line 1: typeset: warning: a: circular name reference environment: line 1: warning: a: circular name reference environment: line 1: warning: a: circular name reference 2 $ zsh -c 'function f { typeset -n a=$1; a=2; }; a=1; f a; echo "$a"' 2 

local itself is from the Almquist shell and the ksh equivalent (which predates the Almquist shell) is typeset (which as noted above, in ksh93 only introduces a local (static) scope in functions declared with the function fname { ...; } syntax; without the typeset, variables refer to the global scope, not the scope of the caller).

In any case, pdksh, at the time it was still maintained, like ksh88 never had support for name references.

But like in bash/zsh/mksh/ash..., it's not as needed as it is in ksh93 as those shells do dynamic scoping, so functions can readily access variables of their caller as long as they don't shadow them with their own local variables. In mksh or bash, namerefs can be seen as syntactic sugar to avoid having to use eval.

For instance, instead of:

function myfun { typeset -n __var="$1" typeset __local_var=whatever __var=$(cmd that computes new value)$__local_var } myfun myvar 

(note the __ prefix to reduce the risk of clash in mksh/bash).

You can do:

function myfun { typeset __var="$1" typeset __local_var=whatever eval "$__var="'$(cmd that computes new value)$__local_var' } myfun myvar 

Note the need to put the $(... inside single quotes so it's passed literally to eval³. Failing to do so is a sure way to introduce command injection vulnerabilities. namerefs help you with that as they make it easier to make sure only the referenced variable name is subject to evaluation (there's still a potential for ACE vulnerability if you don't sanitise the variable name though).

Using a temporary variable and limiting yourself to eval "$__var"'=$__tmp' to reduce the chance of mistake is good practice as in:

function myfun { typeset __var="$1" __var_value typeset __local_var=whatever eval "__var_value=\${$__var}" printf '%s\n' "$__var initially contained $__var_value" __var_value=$(cmd that computes new value)$__local_var printf '%s\n' "new value to be $__var_value" eval "$__var"'=$__var_value' } myfun myvar 

¹ when using ksh-style functions (defined with function fname { ...; }), there's no scoping at all in Bourne-style functions (defined with fname() command).

² which was closed source and expensive; and still is closed source contrary to ksh93 whose source was released circa 2000, though nowadays you can find code of some of its versions on archive.org and elsewhere.

³ and the double quotes around $__var to prevent split+glob as usual.

0

I think in pdksh you can only use the ksh88 features, and there’s no pass-by-reference.'like you want.

You can try using eval instead.

If you really want nameref, you need ksh93 or newer.

PD-ksh' is a clone of the AT&T Korn shell. At the moment, it has most of the ksh88 features, not much of the ksh93 features, and a number of its own features.

pdksh is the public domain Korn shell, a clone of the Korn Shell. It has most of the ksh88 features, and almost none of the ksh93 features.

2
  • you mean like printf -v $1 %s $result_to_put_back_into_the_ref? Commented Aug 27 at 8:53
  • @Harry, I've removed the reference to printf -v, that's a feature originating in bash, extended by zsh and copied by ksh93 recently, but not found in pdksh, which doesn't even have a printf builtin. Commented Sep 4 at 7:49

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.