2

Some background: When writing reusable code in bash, I saw some shell scripts containing several definitions of functions. Sometimes I can't figure out if the functions defined in a script are related, or the script is actually a mess, or I don't know how to organize multiple functions in one script yet (what are your tips for that purpose?). So I am thinking of writing a script containing just one function definition, and I am guessing I will have to organize multiple files. Which one is better, organizing multiple function definitions in one script or organizing multiple script files each with one function definition?

In the following, I will assume only writing a script containing just one function definition.


My actual questions start from here:

When writing a script containing just one function definition, I can write

#! /bin/bash

function myfunc() {
    echo $1, $2, $3
    # do some work...
}

and then I can use it by

source /path/to/myscript
myfunc 1 2 3

Alternatively, can I achieve the same goal of code reusability, by eliminating the function definition and moving the code in the function body to the script:

#! /bin/bash

echo $1, $2, $3
# do some work...

and then I can use it by

source /path/to/myscript 1 2 3

? The second way looks more concise than the first way in terms of both script content and usage, but I am not sure if someone may dislike the second way for some good reason. I was wondering what some advantages and disadvantages of the above two ways are? What is your recommendation in practice?

If you use the first way in practice, would you name your script the same as your function, i.e. if your script containing a function named myfunc, do you name the script as myfunc too?

Thanks.

  • Do you exec this block of code more than 5 times in script. If yes make it function – Romeo Ninov Nov 14 '18 at 17:04
  • Thanks. In that situation, I can still get away without creating a function, by running source /path/to/myscript 1 2 3 five times – Tim Nov 14 '18 at 17:06
  • True, but you will decrease portability and maintainability of the script :) My personal preference is to keep all in one file – Romeo Ninov Nov 14 '18 at 17:07
  • Could you elaborate how that "decrease portability and maintainability of the script"? What does "keep all in one file" mean? – Tim Nov 14 '18 at 17:08
  • 1
    I would only break the functions out into another file if multiple scripts will be using the same functions. Possibly if the script will become too long with the functions included (but then some people will say to use a "real" programming language instead anyway). – Jesse_b Nov 14 '18 at 17:09
3

I do both depending on the circumstance. For most cases I will declare the functions in the same script that will be using them, however I do have a "toolkit" of scripts that has a file of variables and functions to be shared between all the scripts.

To Function or Not to Function

The main purpose of a function is DRY (Don't repeat yourself). If you have some code that will be used more than once in your script it should be in a function.

Using functions on non-repeating code is a matter of preference. Some people (myself included) consider it to be more neat. Additionally I think it closer resembles the way "real" programming languages are used.

Google's Shell Style Guide states that if your code contains at least one function you should use a "main" function as a wrapper for all your code. (Essentially your script will only be a series of function declarations ending with a single call to main)

Declaring functions (or writing code) inside of a single script

  • Simple (Anyone looking at the code will be able to easier track down what each function does)
  • Increases portability (Only one file needs to be moved from system to system)

If you are writing a stand alone script to perform a single task this is probably the way to go.

Declaring functions inside of a separate file

  • DRY (Don't repeat yourself)
  • Can be easier to manage

If you are creating a set of tools that share a lot of common functions this is likely the way to go.

In my example I have functions that are used by all or at least most of the scripts in the toolkit. This includes some functions that query our inventory management system and return information about servers such as IP address, OS version, etc. This information is useful for many of the tools so it makes sense to declare such functions in one file instead of all of the files individually. Additionally we recently made changes to the inventory management system, so instead of having to change 10+ different files I only had to change the common file to query the new system, the other files still work properly without modification.

Some disadvantages of this is that it is more complex. Each file has the following statement to source this common file:

if [[ -f "${0%/*}/lib/common.sh" ]]; then
    . "${0%/*}/lib/common.sh"
else
    echo "Error! lib/common.sh not found!"
    exit 1
fi

If users grab the toolkit and modify the directory structure or don't grab the entire directory structure the tools will not work as intended because they will be unable to source the common file.

  • Thanks. When you write a script containing just one function definition, you can write the script without function definition too, as shown in the latter part of my post. Which one is better, with one function definition, or without any function definition? – Tim Nov 14 '18 at 17:37
  • @Tim: I've updated my question. – Jesse_b Nov 14 '18 at 17:47
0

This will be a long answer and not one of the yes-or-no type. It describes different programming aspects that Bash functions cover, which should be taken into consideration while picking the line of action.

At the end you can find a demo of a single function definition in a separate, but parametrised, sourced file.


A function is:

  • a named
  • block of code
  • which can be reused (called many times)

Heeding the above, what is it good for, and does it have alternatives?

  1. Logical organisation of code

    Grouping together a batch of commands intended for a certain purpose makes a lot of sense. After all, that's one of the meanings of the term function - something has such and such function. The same aim can be achieved in other ways though:

    • Commands can be grouped visually, eg. occupying consecutive lines and separated from the rest of the code with empty lines and comments. Or just put on the same line with a comment following at the end.
    • They can be grouped in a separate file, which can be executed or sourced.
    • They could be just blocked using the curly braces { ... }.
    • They could even be grouped and executed in their separate subshell, when put inside the subshell parentheses ( ... ).1
  2. Batch operations

    Functions give the advantage of easy bulk I/O redirection with the standard Bash operators. Blocks, subshells, script execution and sourcing do too, but for the merely visual command groups I/O file descriptor changes have to be applied with exec.

  3. Localisation

    This is one of the things that make functions special in opposition to simple blocks. Being able to make variables local. And this is often a good programming practice, as it keeps variable visibility at its lowest, which reduces clutter in the wider namespace and introduces isolation. Which, in turn, is a valuable security feature.

    Separate scripts and subshells are an alternative here. Sourced files too, with regard to the positional parameters.

  4. Parametrisation

    Each function gets its own set of the positional parameters $1, $2, etc. This allows for functions' behaviour to be modified in an explicit way in the command call.

    The alternatives here include stand-alone scripts and sourced files.

  5. Code identification

    A function's name is unique, and that means not just their unambiguous recognition, but also the capability of them being executed. And many times of needed. Neither the {} blocks, nor the subshell, nor plain text grouping offers this - unless they're embraced by loops. Though scripts and sourced files do.

  6. Information

    Finally, both the function name and any comments it may contain account for the code's intelligibility. All the other options present leeway here, but anonymous solutions like the subshell and the {} block can't facilitate a formal title (and address) for a code block.


Here's an example of how a single function might be reasonably placed in a separate sourced file.

case "$1" in
    a)
        f () { echo "This function was defined in a)."; }
        ;;
    b)
        f () { echo "This function was defined in b)."; }
        ;;
    *)
        f () { echo "This function was defined with no recognised argument."; }
        ;;
esac

This conditional definition acts like a function factory. The file sourced from elsewhere installs the f function there. All this code might be included in another function and as it stands, there would be little difference. But once the function generator gets large enough to become an entity in its own right, this approach combines the best of the two: the function and the sourced file.


See also

In fact Bash allows a similar function definition: f () ( ...;). So defined, the f function will be executed in its own subshell.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.