Skip to content

Bash utility functions

Bash world is often the realm of the do it yourself. 😇 This is belonging to the nature of the language as it is very unusual to have libraries with functionality to be reused. However, it is very simple to provide the codebase with some utility function tailored on the needs of the project. In the following you will find an overview of what is available in the codebase.

These functions are tailored on the project

The following functions should not be considered as a library. Actually most of them would not work if copied and pasted in another project. Be aware that some of them depend on project-specific aspects (e.g. they use the logger or they assume that the shell used options are on) and, sometimes, make use of other utility functions.

Mimicking boolean functions

In Bash conditionals are based on exit codes of commands and therefore a function which returns either 0 (success) or 1 (failure) can directly be used in a conditional clause, e.g. a if statement. When the function returns 0, the condition will be evaluated as true, and when the function returns 1, the condition will be evaluated as false.

Array utilities

Element_In_Array_Equals_To

Test if an array contains a given element using string comparison. The function returns 0 if the element is in the array, 1 otherwise.

if Element_In_Array_Equals_To 'element' "${array[@]}"; then
    # 'element' is in array
fi
Element_In_Array_Matches

Test if an array contains a given element using regex comparison. The function returns 0 if at least one element in the array matches the regular expression, 1 otherwise.

if Element_In_Array_Matches '^file_[0-9]+' "${array[@]}"; then
    # array contains one entry matching the regex
fi
Call_Function_If_Existing_Or_Exit

Check if the given function exists and if so call it forwarding all arguments. Exit with an internal error otherwise.

# These two are equivalent if the function 'Mystery_Function' is defined
Call_Function_If_Existing_Or_Exit 'Mystery_Function' 'Arg_1' 'Arg_2' 'Arg_3'
Mystery_Function 'Arg_1' 'Arg_2' 'Arg_3'
Call_Function_If_Existing_Or_No_Op

Check if the given function exists and if so call it forwarding all arguments. This is a no-operation if the function is not defined.

# These two are equivalent if the function 'Mystery_Function' is defined
Call_Function_If_Existing_Or_No_Op 'Mystery_Function' 'Arg_1' 'Arg_2' 'Arg_3'
Mystery_Function 'Arg_1' 'Arg_2' 'Arg_3'
Ensure_That_Given_Variables_Are_Set

Check if the given variables are set, i.e. have been declared. Exit with an internal error if at least one variable is not set.

Ensure_That_Given_Variables_Are_Set 'var_1' 'var_2' 'var_3'
Ensure_That_Given_Variables_Are_Set_And_Not_Empty

Check if the given variables are set and not empty. Exit with an internal error if at least one variable is not set or set but empty.

What does empty mean?

A normal variable is empty if it is set to ''. An array is considered empty if it has size equal to 0.

Ensure_That_Given_Variables_Are_Set_And_Not_Empty 'var_1' 'var_2' 'var_3'
Make_Functions_Defined_In_This_File_Readonly

Extract all functions defined in the file where called and mark them as readonly.

An important assumption

Only functions defined as function Name_Of_The_Function() and the braces on new lines are recognized. Accepted symbols in the function name are letters, _, : and -.

Make_Functions_Defined_In_This_File_Readonly
Print_Not_Implemented_Function_Error

Print an error about the caller function using the logger.

Print_Not_Implemented_Function_Error

All functions have the same interface

The following functions need to be called with the YAML string as first argument and the section key(s) as remaining argument(s). As it is assumed everywhere that no key contains a period (or a space), keys can be passed to this function also already concatenated (or in a mixed way).

Has_YAML_String_Given_Key

Test if a YAML string contains a given key. If the YAML string is invalid, an error is printed and the function exits. The function returns 0 if the key is present in the YAML string, 1 otherwise.

yaml_string=$'section:\n  key: 42\n'
if Has_YAML_String_Given_Key "${yaml_string}" 'section' 'key'; then
    # this is executed
fi
Read_From_YAML_String_Given_Key

Read a given key from a YAML string. If the YAML string does not contain the key (or it is invalid) the function exits with an error. The read key is printed to standard output.

yaml_string=$'section:\n  key: 42\n'
key_value=$(Read_From_YAML_String_Given_Key "${yaml_string}" 'section' 'key')
echo "${key_value}"  # <-- this prints '42'
key_value=$(Read_From_YAML_String_Given_Key "${yaml_string}" 'section.key')
echo "${key_value}"  # <-- this prints '42'
Print_YAML_String_Without_Given_Key

Remove a given key from a YAML string. If the YAML string does not contain the key (or it is invalid) the function exits with an error. The new YAML string is printed to standard output.

yaml_string=$'a: 17\nb: 42\n'
yaml_string=$(Print_YAML_String_Without_Given_Key "${yaml_string}" 'b')
echo "${yaml_string}"  # <-- this prints 'a: 17'

Output utilities

Print_Line_of_Equals

Print a line of equal signs to the standard output. Function interface:

  1. Length in characters of the line.
  2. Prefix to be printed before the line (optional, default: '').
  3. Postfix to be printed after the line (optional, default: '\n').
Print_Line_of_Equals 80 '\e[96m    ' '\e[0m\n'
Print_Centered_Line

Print a string horizontally centered in the terminal or in the provided length. Function interface:

  1. String to be printed.
  2. Length in characters of the line (optional, default: terminal width).
  3. Prefix to be printed before the line (optional, default: '').
  4. Padding character to fill the line left and right of the string (optional, default: ' ').
  5. Postfix to be printed after the line (optional, default: '\n').
Print_Centered_Line 'Hello world!' 80 '\e[96m    ' '=' '\e[0m\n'

File utilities

Remove_Comments_In_File

Remove comments starting with a given character (default '#') in a given file. In particular:

  • Entire lines starting with a comment (possibly with leading spaces) are removed.
  • Inline comments with any space before them are removed.

Think before using this function!

This function considers as comments anything coming after any occurrence of the specified comment character and you should not use it if there might be occurrences of that character that do not start a comment! For the hybrid handler configuration such a basic implementation is enough.

Remove_Comments_In_File 'config.yaml'
Remove_Comments_In_File 'doc.tex' '%'
Functions to ensure presence or absence of files or folders

There are some functions that add a level of abstraction to testing for file or folders existence or absence. Their interface has been unified. In particular, the arguments are interpreted as file or folder names and each is tested. However, if an argument is --, then the arguments before it are interpreted as add-on message to be printed in case of error (one per line).

  • Ensure_Given_Files_Do_Not_Exist
  • Ensure_Given_Files_Exist
  • Ensure_Given_Folders_Do_Not_Exist
  • Ensure_Given_Folders_Exist
  • Internally_Ensure_Given_Files_Do_Not_Exist
  • Internally_Ensure_Given_Files_Exist

The last ones fail with an internal error instead of a normal fatal one. Note that symbolic links are accepted as arguments and the entity of what they resolve to is tested. Links resolution is done using realpath -m before testing the entity (the option -m accepts non existing paths).

Miscellaneous

Strip_ANSI_Color_Codes_From_String

Remove ANSI color codes from the given string. The cleaned up string is printed to standard output.

Strip_ANSI_Color_Codes_From_String $'\e[96mHi\e[0m' # <-- this prints 'Hi'
Print_Option_Specification_Error_And_Exit

Print a fatal error about the given option using the logger and exits.

Print_Option_Specification_Error_And_Exit '--filename'