How to make nested function local?

Hi,

If I declare a function inside another function, it overwrites any previously declared function with the same name. This is NOT what I want.

Example:

#!/bin/bash

_test() { echo test; }

_myf() {
    # I'm using the same name as the other function.
    _test() { echo local test; }
    # I'm expecting this call to print "local test"
    _test
}

# I'm expecting this call to print "test"
_test
# I'm calling myf hopping it will print "local test" but leave function "_test" unchanged.
_myf
# I'm expecting this call to still print "test" but function _test has been irredeemably changed.
_test

What I hop to get is:

test
local test
test

What I get is:

test
local test
local test

How can I make sure that any function I declare inside a function stays local to the later function?

Thanks for you help.
Santiago

Bash has local variables, but not local functions.
As a work-around you can try a classic sub-shell (where a modern implementation might avoid the overhead of an extra process).

_myf() {
    # I'm using the same name as the other function.
    (  # subshell start
    _test() { echo local test; }
    # I'm expecting this call to print "local test"
    _test
    )  # subshell end
}
1 Like

Thanks MadeInGermany,

I didn't think about your approach. And I didn't know there was no such local functions.

Because my function is pretty long, I'm going to stick to another workaround which is to use a rather unusual function name like in:

_myf() {
    _test_hopefully_no_one_ever_uses_that_name_ipjjebyfevljscsdgvsg() {
        echo local test
    }
    echo foo
    echo bar
    echo baz
    _test_hopefully_no_one_ever_uses_that_name_ipjjebyfevljscsdgvsg
    echo foo
    echo bar
    echo baz
    _test_hopefully_no_one_ever_uses_that_name_ipjjebyfevljscsdgvsg
}

Hi.

The last example on page Functions suggests that bash cannot do this.

The shell ksh has a namespace facility. So if you wrote your code as in file user6:

#!/bin/ksh
#!/bin/bash

_test() { echo test; }

_myf() {
  # I'm using the same name as the other function.
  namespace mine {
    _test() { echo local test; }
    # I'm expecting this call to print "local test"
    _test
  }
}

# I'm expecting this call to print "test"
_test
# I'm calling myf hopping it will print "local test" but leave function "_test" unchanged.
_myf
# I'm expecting this call to still print "test" but function _test has been irredeemably changed.
_test

exit $?

then produces:

$ ./user6 
test
local test
test

For a system like:

OS, ker|rel, machine: Linux, 3.16.0-4-amd64, x86_64
Distribution        : Debian 8.7 (jessie) 
ksh 93u+

And, apparently if you needed to use the namespace function outside of the namespace, you could use it as .mine._test .

I have not used the namepace idea before, but it seems to work. See man ksh for some details.

Best wishes ... cheers, drl

1 Like

Thanks drl,

Never used ksh before. Just gave your code a test and it does what I want.

Cheers
Santiago

1 Like

Hi, Santiago.

Good to hear that it worked for you.

Note that there may be some differences between ksh and bash , so keep that man page handy :slight_smile:

Best wishes ... cheers, drl

Does not work with the ksh on Solaris.
Solaris 11 ksh "Version JM 93u 2011-02-08" takes the namespace mine { } , but the _test inside it runs the global function.
However the .mine._test works.
And the namespace feature is not mentioned in the ksh man page.
--
Regarding the uniqueness of function names:
I would prefix the new function with the current function's name: _myf__test { }

Hi.

The more complete version string for the Linux ksh that I used is Version AJM 93u+ 2012-08-01

Also found on:

CentOS 6.4
Fedora 23
Ubuntu 14.04.4
Slackware 14.1
FreeBSD 11.0
macOS 10.12.5

Perhaps a more recent version of ksh for Solaris is available from a source other than Oracle.

Best wishes ... cheers, drl

Why do you want to have a local function? From my personal experience, the main use is for passing around (anonymous) function objects or closures, which can't be done in bash anyway. Where is the problem to just name it differently?