Bash: Nested functions and including other scripts

Hello.

I have the following problem with bash code:

function fl1_load_modules_and_get_list()
...........
	for module in $FL_MODULES_TO_PROCESS
	do
		source "${FL_MODULE_DIR}/${module}/module.sh"
	done
...........
}

function fl1_handle_install
{
	local FL_MODULES_TO_INSTALL=$(fl1_load_modules_and_get_list $1)
	
	#Executing pre-install routines
	for module in $FL_MODULES_TO_INSTALL
	do
		$(fl_mod_${module}_pre_install)
	done
}

First function is called inside the second one. First function loads scripts in which functions with predefined names reside. The second function calls the first and then attempts to call functions with names, that must be loaded with first one.
Loading files with source seem to complete successfully because I have

set -o errexit
set -o nounset
set -o pipefail

at the main script.

But when $(fl_mod_${module}_pre_install) call is done, I can see a message like /usr/local/fractal/fl/lib/handle_install.sh: line 8: fl_mod_php_pre_install: command not found
Somehow functions loaded by the first function are not imported into the global scope... Any idea why?

Thanks.

Use the standard syntax to define functions:

fl1_load_modules_and_get_list()

Please post real code so that it can be copied verbatim.

Do you really want to execute the output of the command?

Here is the source. I would appreciate if you will help me to get it working. I am starting to learn bash and this seem to be a good exercise for me.

I have forgotten to add
execute

f-linsey install all

And the error I was talking about appears.

$ f-linsey install all
bash: f-linsey: command not found
$ ./f-linsey install all
bash: ./f-linsey: No such file or directory

This is overly complex for a learning exercise.

I would recommend that you learn POSIX shell scripting first, then add bash extras as you find them necessary or helpful.

I have gone over the top level script from the zip file and made a few comments:

#!/bin/sh

Since you use bash-specific code in the script, you should specify
bash in the shebang.


# Setting bash options
set -o errexit
set -o nounset
set -o pipefail

# Getting full path to current script
prg=$0
if [ ! -e "$prg" ]; then
  case $prg in
    (*/*) exit 1;;
    (*) prg=$(command -v -- "$prg") || exit;;
  esac
fi

There is rarely, if ever, a good reason to need the location of the running script.

When there is, it should be done in a wrapper that is in a standard location, e.g., /usr/local/bin/linsey, and contain something like:

cd /path/to/working/directory
./f-linsey.sh
# Setting variables
FL_THIS_DIR=$(
  cd -P -- "$(dirname -- "$prg")" && pwd -P
) || exit

Why do you need $FL_THIS_DIR?

In a POSIX shell (which bash is), the current directory is always in ${PWD}.

FL_LIB_DIR="${FL_THIS_DIR}/lib"
FL_FLINSEY_VERSION="1.0"

# Loading libraries
if [ ! -d "${FL_LIB_DIR}" ];
then
	echo "ERROR: library directory does not exist. F-Linsey was not properly installed."
	exit 1
fi

# Including important library files
source "${FL_LIB_DIR}/errors.sh"
source "${FL_LIB_DIR}/handle_help.sh"
source "${FL_LIB_DIR}/modules.sh"

# Checking if modules directory exist
FL_MODULE_DIR="${FL_THIS_DIR}/modules"
if [ ! -d "${FL_MODULE_DIR}" ]
then
	fl1_err_no_modules

Using a function for a trivial piece of code that is only used once
makes the script harder to understand.

Put the body of the function in-line; it's only two lines.

fi
# Parsing command line

# Checking if parameters passed
if [ $# -lt 1 ]
then
	fl1_handle_help

You should keep lines shorter than 80 characters for your help text.

fi
# Reacting to command
FL_COMMAND=$1
shift

# Checking if command handler exists
if [ ! -f "${FL_LIB_DIR}/handle_${FL_COMMAND}.sh" ]
then
	fl1_err_unknown_command $FL_COMMAND
fi

Ditto.

# Loading command handler file and calling handler
source "${FL_LIB_DIR}/handle_${FL_COMMAND}.sh"

Rather than bash-specific code, use the portable dot command:

. "${FL_LIB_DIR}/handle_${FL_COMMAND}.sh"
fl1_handle_${FL_COMMAND} $*

echo "F_Linsey finished."

Now, which line causes the error?

Thank you for valuable comments!

Sorry, I meant
./f-linsey.sh install all
Of course, it wouldn't start without .sh.

Line, which causes error is

fl_mod_${module}_install

in lib/handle_install.sh

It just says, that command not found. But specific module /modules/httpd/module.sh should be loaded to that moment.

I declared $FL_THIS_DIR to make modules and library directory from it to ensure script can be executed from every place in filesystem, not only from current directory. But now I think I don't need it :wink:
I am not yet familiar with Linux and this style of code seem to come from PHP :slight_smile:

Details like that can cause a script to fail.

Should be???? Didn't you check?

Add a line to the file containing it to confirm that it is indeed loaded:

echo modules/httpd/module.sh
function fl_mod_httpd_order()
{
        echo "001000"
}

Or put set -x in the script to turn on debugging.

It has nothing to do with Linux; POSIX shell scripting is common to all *nix variants (and some other systems).

It is nothing like PHP.

Some further comments:

Some of the files are missing a final newline. (It will probably not make a difference, but it should be there.)

You use a mixture of syntaxes for defining functions (POSIX, KSH and a combination of the two that is only valid in bash). Stick to the POSIX standard form:

function_name() COMPOUND_COMMAND

Where do you call the function fl1_load_modules_and_get_list?

fl1_load_modules_and_get_list() is called from lib/handle_install.sh
For each module there source is included by

source "${FL_MODULE_DIR}/${module}/module.sh"

Later for each module

fl_mod_${module}_pre_install

is called, which in turn fails...

The line that calls it is:

$(fl_mod_${module}_pre_install)

It is called in a subshell and therefore is not loaded into the running environment.

How to distinguish subshell calls from direct calls? $() is always a subshell?

man bash:
COMMAND EXECUTION ENVIRONMENT
...
Command substitution, commands grouped with parentheses, and asynchronous commands are invoked in a subshell environment that is a duplicate of the shell environment...

Thank you for links.