74

I have the following code

func1(){
    #some function thing
    function2(){
        #second function thing
    }
}

and I want to call function2 but I get an error function2 : not found

Is there a solution?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
tiranodev
  • 943
  • 2
  • 8
  • 15
  • Similar for variables: http://stackoverflow.com/questions/18597697/posix-compliant-way-to-scope-variables-to-a-function-in-a-shell-script , related on SU http://unix.stackexchange.com/questions/104755/how-can-i-create-a-local-function-in-my-bashrc – Ciro Santilli OurBigBook.com Jul 09 '15 at 10:15

6 Answers6

97

Limit the scope of the inner function

Use function defined with parenthesis () instead of braces {}:

f() (
  g() {
    echo G
  }
  g
)

# Ouputs `G`
f
# Command not found.
g

Parenthesis functions are run in sub-shells, which have the same semantics of () vs {}, see also: Defining bash function body using parenthesis instead of braces

This cannot be used if you want to:

  • set variables
  • exit
  • cd

as those are lost in the created sub-shell.

See also: bash functions: enclosing the body in braces vs. parentheses

TODO: without subshell

Bash has local variables feature which allows allow you to not have to create a subshell by using {} instead of () as in:

f() {
  local g=G
  echo $g
}

# Ouputs `G`
f
# Outputs empty line, `$g` is undefined.
echo $g

this can be:

  • faster
  • more convenient if you want to modify the parent shell in other aspects, e.g. with exit or modifying other globals

Unfortunately, there don't seem to be an analogue for functions: Achieve Local Function

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
69

Function definitions in bash don't work the way function definitions work in many other languages. In bash, a function definition is an executable command which defines the effect of a function (replacing any previous definition), in much the same way that a variable assignment command defines the value of a variable (replacing any previous definition). Perhaps this example will clarify what I mean:

$ outerfunc1() {
> innerfunc() { echo "Running inner function #1"; }
> echo "Running outer function #1"
> }
$ outerfunc2() {
> innerfunc() { echo "Running inner function #2"; }
> echo "Running outer function #2"
> }
$
$ # At this point, both outerfunc1 and outerfunc2 contain definitions of
$ # innerfunc, but since neither has been executed yet, the definitions
$ # haven't "happened".
$ innerfunc
-bash: innerfunc: command not found
$
$ outerfunc1
Running outer function #1
$ # Now that outerfunc1 has executed, it has defined innerfunc:
$ innerfunc
Running inner function #1
$
$ outerfunc2
Running outer function #2
$ # Running outerfunc2 has redefined innerfunc:
$ innerfunc
Running inner function #2

Now, if you didn't already know this, I'm pretty sure this wasn't your reason for nesting function definitions. Which brings up the question: why are you nesting function definitions at all? Whatever effect you expected nested definitions to have, that's not what they do in bash; so 1) unnest them and 2) find some other way to accomplish whatever you were trying to get the nesting to do for you.

Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151
23

In the question case I suppose that you were trying to call function2 before it is defined, "some function thing" should have been after the function2 definition.

For the sake of discussion, I have a case where using such definitions can be of some use.

Suppose you want to provide a function that might be complex, its readability could be helped by splitting the code in smaller functions but you don't want that such functions are made accessible.

Running the following script (inner_vs_outer.sh)

#!/bin/bash
function outer1 {
    function inner1 {
       echo '*** Into inner function of outer1'
    }
    inner1;
    unset -f inner1
}

function outer2 {
    function inner2 {
       echo '*** Into inner function of outer2'
    }
    inner2;
    unset -f inner2
}
export PS1=':inner_vs_outer\$ '
export -f outer1 outer2

exec bash -i

when executed a new shell is created. Here outer1 and outer2 are valid commands, but inner is not, since it has been unset exiting from where you have outer1 and outer2 defined but inner is not and will not be because you unset it at the end of the function.

$ ./inner_vs_outer.sh
:inner_vs_outer$ outer1
*** Into inner function of outer1
:inner_vs_outer$ outer2
*** Into inner function of outer2
:inner_vs_outer$ inner1
bash: inner1: command not found
:inner_vs_outer$ inner2
bash: inner2: command not found

Note that if you define the inner functions at the outer level and you don't export them they will not be accessible from the new shell, but running the outer function will result in errors because they will try executing functions no longer accessible; instead, the nested functions are defined every time the outer function is called.

Teudimundo
  • 2,610
  • 20
  • 28
  • 4
    Nice and [POSIX](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_29). Shame that it destroys external functions called `inner1`, so should be coupled with a naming convention like `__` prefix. – Ciro Santilli OurBigBook.com Jul 09 '15 at 10:16
14

Don't nest function definitions. replace with:

$ cat try.bash 
function one {
  echo "One"
}

function two {
  echo "Two"
}

function three {
   one
   two
}

three
$ bash try.bash 
One
Two
$ 
Kevin
  • 6,665
  • 1
  • 17
  • 14
Charlie Martin
  • 110,348
  • 25
  • 193
  • 263
  • 22
    When you say *'Don't nest the functions'*, is it because bash does not support nested functions? (I'm just curious as I'm not very familiar with bash) – Russell Dias Dec 08 '11 at 04:20
  • 1
    my code need to call a function inside another.. i cant split all – tiranodev Dec 08 '11 at 04:21
  • 2
    You can call the functions from within each other. You just can't define them within each other as in the original post and expect it to work. – Michael Hoffman Dec 08 '11 at 04:24
  • 1
    See http://www.tldp.org/LDP/abs/html/functions.html. Sorry, but search for "It is even possible to nest a function within another function, although this is not very useful." Bash is not an OO language. Also it is not clear what your really trying to acomplish here, maybe a re-write of you problem will help the bash regulars determine if bash can help. Good luck.! – shellter Dec 08 '11 at 04:25
  • 2
    I have a huge code with a lot of functions nested and i want to add command line arguments for some functions that are inside another – tiranodev Dec 08 '11 at 04:32
  • 1
    You wrote: 'I have a huge code with a lot of functions nested ...i want to add command line arguments ...' Per the tldp.org page posted above, this is probably possible, but will almost certainly not work as you need. ... 'I have a huge code' ... And you had this working in a shell-scripting environment? Now we're really interested. :-) . Please edit your question to clarify your needs. Minimal examples, samples, error messages are very helpful. Good luck. – shellter Dec 08 '11 at 04:59
  • 1
    I've never thought to nest functions in bash before, but i just tried this and it worked. I was able to call f2, defined in f1, from both inside and outside f1. What version of bash do you have? – frankc Dec 08 '11 at 17:44
  • Okay, I give up. What are you trying to *do*? – Charlie Martin Dec 09 '11 at 15:51
  • The point here is that we only want nested_function() available once we've carried out some initialization for it by calling outer_function(). OO in Bash - who would have thought.... – Asfand Qazi Feb 11 '20 at 17:21
  • 1
    @JamesMadison Unfortunately, nesting functions does not make them local, once defined they become part of the global namespace. – PePa Jan 05 '22 at 10:40
  • 1
    @PePa Agreed! My original comment was so poorly worded that I deleted it. I meant to say that I'm still a fan of nesting so that when you're coding, visually, you see the relationship and intent of the functions. But yes, they all go to the flat global namespace. Which is annoying. – James Madison Jan 08 '22 at 12:27
  • nested function is working in the newest bash version. This answer should be updated! – MaXi32 Jan 02 '23 at 11:33
3

If you're nesting a function, say function2 inside function1, it doesn't become available until function1 is called. Some people might consider this a feature, as you can do something like "unset function2" at the end of function1 and its scope is completely local to that function (can't be called from elsewhere). If you want to call the function elsewhere, there's probably no need to nest it anyway.

Brian
  • 91
  • 3
0

Your code should work as written provided you only call the nested function after it's defined.

As said in another answer, a func(){<...>} statement is an executable statement that defines a name (in the global scope) associated with the function. Just like if you defined a variable.

So, you can use func2 anywhere in the code after the func2(){<...>} statement has run:

#func2 not defined
func1(){
    #not defined
    func2(){<...>}
    #defined
}
#not defined
func1
#defined
func3(){
    #only defined if func3 is called after `func2(){<...>}' has run
}
ivan_pozdeev
  • 33,874
  • 19
  • 107
  • 152