37

Is it possible to call a function which is declared below in bash?

Example

if [ "$input" = "yes" ]; then
    YES_FUNCTION
elif [ "$input" = "no" ]; then
    NO_FUNCTION
else
    exit 0;
fi

YES_FUNCTION()
{
  .....
  .....
}

NO_FUNCTION()
{
  .....
  .....
}
Siva
  • 9,077

4 Answers4

79

Like others have said, you can't do that.

But if you want to arrange the code into one file so that the main program is at the top of the file, and other functions are defined below, you can do it by having a separate main function.

E.g.

#!/bin/sh

main() {
    if [ "$1" = yes ]; then
        do_task_this
    else
        do_task_that
    fi
}

do_task_this() {
    ...
} 
do_task_that() {
    ...
} 

main "$@"; exit

When we call main at the end of file, all functions are already defined. Explicitly passing "$@" to main is required to make the command line arguments of the script visible in the function.

The explicit exit on the same line as the call to main is not mandatory, but can be used to prevent a running script from getting messed up if the script file is modified. Without it, the shell would try to continue reading commands from the script file after main returns. (see How to read the whole shell script before executing it?)

ilkkachu
  • 138,973
  • 11
    With Bash scripts I often use [[ ${BASH_SOURCE[0]} = "$0" ]] && Main "$@" to call the main function so I can source it in another script without Main being executed. Then I can either reuse the functions or write tests to check them. – BlackJack Jun 13 '18 at 14:10
  • 14
    Having main "$@"; exit (with exit on the same line as main) is also useful as a safeguard against the file being modified while it is interpreted. – Stéphane Chazelas Jun 13 '18 at 15:03
  • @StéphaneChazelas "interpreted" as in executing or parsing? I thought shells parse the files and then execute the AST in memory. Can you modify a loop body in a file while a shell is executing and see the effects? I just tried that with bash and it didn't work... Oh! But it did work to add a new top-level statement while it was sleeping from a previous statement! – JoL Jun 13 '18 at 15:18
  • 2
    @JoL, what is read is not read again, and the shell will need to read and parse the whole text of a loop before starting to run it, but then after the loop returns it will carry on reading from the rest of the file at the current position (and if the file has been modified it will screw things up). If everything is in functions, the shell needs to read everything before starting to do anything (except defining those functions), if we put the exit on the same line as main we make sure the shell won't read anything again from the file after main returns. – Stéphane Chazelas Jun 13 '18 at 15:45
  • @StéphaneChazelas I made a question on this to which your comment would be half the answer: https://unix.stackexchange.com/questions/449593/is-parsing-scripts-at-script-runtime-ubiquitous-to-shells-or-present-in-other-in – JoL Jun 13 '18 at 16:05
  • @StéphaneChazelas What does main "$@"; exit as the last line in the file do that main "$@" wouldn't do? OTOH, main "$@"; exit $? would allow main() to return a value that would be passed as the exit status of the script, so that actually adds some value. – Monty Harder Jun 13 '18 at 16:32
  • 1
    @MontyHarder, it doesn't matter if you use main; exit, main; exit $? or main <EOF>, in all cases the exit code of main is used as the exit code of the script. The exit would be just to prevent things getting messed up if someone edits the script while it's running. – ilkkachu Jun 13 '18 at 16:36
22

No, the functions have to exist in the shells environment at the time of calling them.

Google's "Shell Style Guide" has a fix for this:

A function called main is required for scripts long enough to contain at least one other function.

At the very end of the script, after all functions, as the only statement not in a function, you would have

main "$@"

This would call the main function with whatever parameters the script was given. The main function could be located at the top of the script (the style guide says to put it at the bottom, but then again, it says many things).

When the shell gets to the main call, all functions in the script have been parsed and can therefore be called from within the main function.

Kusalananda
  • 333,661
10

No, functions have to be declared before they’re used. Shell scripts are read line by line and acted upon line by line; so a function doesn’t exist until its declaration has been executed.

Stephen Kitt
  • 434,908
  • your are right. the problem is I have 30+ functions in a script. it's quite tricky when we read the code. In C it's comfortable. – Siva Jun 13 '18 at 10:22
  • 3
    You can put your function declarations in another file and source it (. yourfile). – Stephen Kitt Jun 13 '18 at 10:24
  • Yes, I have tried that, but the requirement is to have a single script. – Siva Jun 13 '18 at 10:26
  • @SivaPrasath What's exactly the problem? Just define all the functions, maybe even putting the main code in a function and then the last line shows which function is called and contains the main part of the script. – BlackJack Jun 13 '18 at 13:58
  • @SivaPrasath In C, you don't have bare if statements outside of a function. The function doesn't have to be defined when you declare the if-containing function, just when you call it. – chepner Jun 13 '18 at 14:12
4

The shell has no concept of declaring a function. So you cannot have a forward declaration.

As a consequence, you need to have the function implementation read by the shell before it can be called.

schily
  • 19,173
  • 4
    Technically, some shells (ksh, zsh) have a function autoloading feature which could be seen as some form of declaration (where autoload f declares the function, but its body is only loaded upon the first invocation). That doesn't apply to the OP's bash though. – Stéphane Chazelas Jun 13 '18 at 15:07