Developing in bash has some serious flaws:
- scoping - bash functions are always global
- no exception handling
- larger projects quickly become non-transparent
- ...
- modular import system
- advanced logging (colors, control stdout/stderr, log levels, ...)
- error handling (exceptions, try-catch)
- doc testing inspired by python
- documentation generation
- argument parser
- utility functions
./doc_test.sh array.sh -v
./doc_test.sh
./doc_test.sh -v
Source the core module and use core.import
to import
other modules.
#!/usr/bin/env bash
source path/to/core.sh
core.import <modulename>
core.import <another modulename>
# use modules ...
Currently only an archlinux package is available at the
aur.
After installation all rebash files are available under /usr/lib/rebash/
.
The doc_test and documentation modules are available as
/usr/bin/rebash-doc-test
and /usr/bin/rebash-documentation
.
Modules are single files. The function core.import
guarantees that each module is sourced only once.
All variables and functions defined inside a module should be prefixed with the
module name. E.g. core_import
for the function import
in module core
.
Aliases inside the module are used to define public functions and to have a
convinient way to distinguish the module namespace from the function
(alias core.import="core_import"
).
A typical minimal module looks like this (with filename mockup.sh
):
#!/usr/bin/env bash
source "$(dirname "${BASH_SOURCE[0]}")/core.sh"
core.import logging
mockup_foo() {
echo foo
}
alias mockup.foo="mockup_foo"
Loading modules (i.e. when sourced by the import mechanism) should be side-effect free, so only variable and function definitions should be made at the module level. If the module should be executable, use core.is_main. For example this module does activate exceptions only when run directly, not when being sourced.
#!/usr/bin/env bash
source path/to/core.sh
core.import exceptions
main() {
exceptions.activate
# do stuff
}
if core.is_main; then
main
fi
Write doc_tests for every module and function. Write the tests before writing the implementation.
Use shellcheck to tackle common errors and pitfalls in bash.
The arguments module provides an argument parser that can be used in functions and scripts.
Different functions are provided in order to parse an arguments array.
>>> _() {
>>> local value
>>> arguments.set "$@"
>>> arguments.get_parameter param1 value
>>> echo "param1: $value"
>>> arguments.get_keyword keyword2 value
>>> echo "keyword2: $value"
>>> arguments.get_flag --flag4 value
>>> echo "--flag4: $value"
>>> # NOTE: Get the positionals last
>>> arguments.get_positional 1 value
>>> echo 1: "$value"
>>> # Alternative way to get positionals: Set the arguments array to
>>> # $arguments_new_arguments
>>> set -- "${arguments_new_arguments[@]}"
>>> echo 1: "$1"
>>> }
>>> _ param1 value1 keyword2=value2 positional3 --flag4
param1: value1
keyword2: value2
--flag4: true
1: positional3
1: positional3
arguments.get_flag flag [flag_aliases...] variable_name
Sets variable_name
to true if flag (or on of its aliases) is contained in
the argument array (see arguments.set
)
arguments.get_flag verbose --verbose -v verbose_is_set
>>> arguments.set other_param1 --foo other_param2
>>> local foo bar
>>> arguments.get_flag --foo -f foo
>>> echo $foo
>>> arguments.get_flag --bar bar
>>> echo $bar
>>> echo "${arguments_new_arguments[@]}"
true
false
other_param1 other_param2
>>> arguments.set -f
>>> local foo
>>> arguments.get_flag --foo -f foo
>>> echo $foo
true
arguments.get_keyword keyword variable_name
Sets variable_name
to the "value" of keyword
the argument array (see
arguments.set
) contains "keyword=value".
arguments.get_keyword log loglevel
>>> local foo
>>> arguments.set other_param1 foo=bar baz=baz other_param2
>>> arguments.get_keyword foo foo
>>> echo $foo
>>> echo "${arguments_new_arguments[@]}"
bar
other_param1 baz=baz other_param2
>>> local foo
>>> arguments.set other_param1 foo=bar baz=baz other_param2
>>> arguments.get_keyword foo
>>> echo $foo
>>> arguments.get_keyword baz foo
>>> echo $foo
bar
baz
arguments.get_parameter parameter [parameter_aliases...] variable_name
Sets variable_name
to the field following parameter
(or one of the
parameter_aliases
) from the argument array (see arguments.set
).
arguments.get_parameter --log-level -l loglevel
>>> local foo
>>> arguments.set other_param1 --foo bar other_param2
>>> arguments.get_parameter --foo -f foo
>>> echo $foo
>>> echo "${arguments_new_arguments[@]}"
bar
other_param1 other_param2
arguments.get_positional index variable_name
Get the positional parameter at index
. Use after extracting parameters,
keywords and flags.
>>> arguments.set parameter foo --flag pos1 pos2 --keyword=foo
>>> arguments.get_flag --flag _
>>> arguments.get_parameter parameter _
>>> arguments.get_keyword --keyword _
>>> local positional1 positional2
>>> arguments.get_positional 1 positional1
>>> arguments.get_positional 2 positional2
>>> echo "$positional1 $positional2"
pos1 pos2
arguments.set argument1 argument2 ...
Set the array the arguments-module is working on. After getting the desired
arguments, the new argument array can be accessed via
arguments_new_arguments
. This new array contains all remaining arguments.
Filters values from given array by given regular expression.
>>> local a=(one two three wolf)
>>> local b=( $(array.filter ".*wo.*" "${a[@]}") )
>>> echo ${b[*]}
two wolf
Get index of value in an array
>>> local a=(one two three)
>>> array_get_index one "${a[@]}"
0
>>> local a=(one two three)
>>> array_get_index two "${a[@]}"
1
>>> array_get_index bar foo bar baz
1
Returns a slice of an array (similar to Python).
From the Python documentation: One way to remember how slices work is to think of the indices as pointing between elements, with the left edge of the first character numbered 0. Then the right edge of the last element of an array of length n has index n, for example:
+---+---+---+---+---+---+
| 0 | 1 | 2 | 3 | 4 | 5 |
+---+---+---+---+---+---+
0 1 2 3 4 5 6
-6 -5 -4 -3 -2 -1
>>> local a=(0 1 2 3 4 5)
>>> echo $(array.slice 1:-2 "${a[@]}")
1 2 3
>>> local a=(0 1 2 3 4 5)
>>> echo $(array.slice 0:1 "${a[@]}")
0
>>> local a=(0 1 2 3 4 5)
>>> [ -z "$(array.slice 1:1 "${a[@]}")" ] && echo empty
empty
>>> local a=(0 1 2 3 4 5)
>>> [ -z "$(array.slice 2:1 "${a[@]}")" ] && echo empty
empty
>>> local a=(0 1 2 3 4 5)
>>> [ -z "$(array.slice -2:-3 "${a[@]}")" ] && echo empty
empty
>>> local a=(0 1 2 3 4 5)
>>> [ -z "$(array.slice -2:-2 "${a[@]}")" ] && echo empty
empty
Slice indices have useful defaults; an omitted first index defaults to zero, an omitted second index defaults to the size of the string being sliced.
>>> local a=(0 1 2 3 4 5)
>>> # from the beginning to position 2 (excluded)
>>> echo $(array.slice 0:2 "${a[@]}")
>>> echo $(array.slice :2 "${a[@]}")
0 1
0 1
>>> local a=(0 1 2 3 4 5)
>>> # from position 3 (included) to the end
>>> echo $(array.slice 3:"${#a[@]}" "${a[@]}")
>>> echo $(array.slice 3: "${a[@]}")
3 4 5
3 4 5
>>> local a=(0 1 2 3 4 5)
>>> # from the second-last (included) to the end
>>> echo $(array.slice -2:"${#a[@]}" "${a[@]}")
>>> echo $(array.slice -2: "${a[@]}")
4 5
4 5
>>> local a=(0 1 2 3 4 5)
>>> echo $(array.slice -4:-2 "${a[@]}")
2 3
If no range is given, it works like normal array indices.
>>> local a=(0 1 2 3 4 5)
>>> echo $(array.slice -1 "${a[@]}")
5
>>> local a=(0 1 2 3 4 5)
>>> echo $(array.slice -2 "${a[@]}")
4
>>> local a=(0 1 2 3 4 5)
>>> echo $(array.slice 0 "${a[@]}")
0
>>> local a=(0 1 2 3 4 5)
>>> echo $(array.slice 1 "${a[@]}")
1
>>> local a=(0 1 2 3 4 5)
>>> array.slice 6 "${a[@]}"; echo $?
1
>>> local a=(0 1 2 3 4 5)
>>> array.slice -7 "${a[@]}"; echo $?
1
This function performs a linux change root if needed and provides all kernel api filesystems in target root by using a change root interface with minimal needed rights.
change_root /new_root /usr/bin/env bash some arguments
Perform the available change root program wich needs at least rights.
change_root_with_fake_fallback /new_root /usr/bin/env bash some arguments
Performs a change root by mounting needed host locations in change root environment.
change_root_with_kernel_api /new_root /usr/bin/env bash some arguments
Returns all defined aliases in the current scope.
Return all declared variables and function in the current scope.
E.g.
declarations="$(core.get_all_declared_names)"
IMPORTANT: Do not use core.import inside functions -> aliases do not work TODO: explain this in more detail
>>> (
>>> core.import logging
>>> logging_set_level warn
>>> core.import test/mockup_module-b.sh false
>>> )
+doc_test_contains
imported module c
module "mockup_module_c" defines unprefixed name: "foo123"
imported module b
Modules should be imported only once.
>>> (core.import test/mockup_module_a.sh && \
>>> core.import test/mockup_module_a.sh)
imported module a
>>> (
>>> core.import test/mockup_module_a.sh false
>>> echo $core_declared_functions_after_import
>>> )
imported module a
mockup_module_a_foo
>>> (
>>> core.import logging
>>> logging_set_level warn
>>> core.import test/mockup_module_c.sh false
>>> echo $core_declared_functions_after_import
>>> )
+doc_test_contains
imported module b
imported module c
module "mockup_module_c" defines unprefixed name: "foo123"
foo123
Tests if variable is defined (can also be empty)
>>> local foo="bar"
>>> core_is_defined foo; echo $?
>>> [[ -v foo ]]; echo $?
0
0
>>> local defined_but_empty=""
>>> core_is_defined defined_but_empty; echo $?
0
>>> core_is_defined undefined_variable; echo $?
1
>>> set -o nounset
>>> core_is_defined undefined_variable; echo $?
1
Same Tests for bash < 4.2
>>> core__bash_version_test=true
>>> local foo="bar"
>>> core_is_defined foo; echo $?
0
>>> core__bash_version_test=true
>>> local defined_but_empty=""
>>> core_is_defined defined_but_empty; echo $?
0
>>> core__bash_version_test=true
>>> core_is_defined undefined_variable; echo $?
1
>>> core__bash_version_test=true
>>> set -o nounset
>>> core_is_defined undefined_variable; echo $?
1
Tests if variable is empty (undefined variables are not empty)
>>> local foo="bar"
>>> core_is_empty foo; echo $?
1
>>> local defined_and_empty=""
>>> core_is_empty defined_and_empty; echo $?
0
>>> core_is_empty undefined_variable; echo $?
1
>>> set -u
>>> core_is_empty undefined_variable; echo $?
1
Returns true if current script is being executed.
>>> # Note: this test passes because is_main is called by doc_test.sh which
>>> # is being executed.
>>> core.is_main && echo yes
yes
Computes relative path from $1 to $2. Taken from http://stackoverflow.com/a/12498485/2972353
>>> core_rel_path "/A/B/C" "/A"
../..
>>> core_rel_path "/A/B/C" "/A/B"
..
>>> core_rel_path "/A/B/C" "/A/B/C/D"
D
>>> core_rel_path "/A/B/C" "/A/B/C/D/E"
D/E
>>> core_rel_path "/A/B/C" "/A/B/D"
../D
>>> core_rel_path "/A/B/C" "/A/B/D/E"
../D/E
>>> core_rel_path "/A/B/C" "/A/D"
../../D
>>> core_rel_path "/A/B/C" "/A/D/E"
../../D/E
>>> core_rel_path "/A/B/C" "/D/E/F"
../../../D/E/F
>>> core_rel_path "/" "/"
.
>>> core_rel_path "/A/B/C" "/A/B/C"
.
>>> core_rel_path "/A/B/C" "/"
../../../
Sources a script and checks variable definitions before and after sourcing.
>>> local foo="a
b
a
b
c
b
c"
>>> echo -e "$foo" | core.unique
a
b
c
Usage: variable=$(dictionary.get dictionary_name key)
>>> dictionary_get unset_map unset_value; echo $?
1
>>> dictionary__bash_version_test=true
>>> dictionary_get unset_map unset_value; echo $?
1
>>> dictionary_set map foo 2
>>> dictionary_set map bar 1
>>> dictionary_get map foo
>>> dictionary_get map bar
2
1
>>> dictionary_set map foo "a b c"
>>> dictionary_get map foo
a b c
>>> dictionary__bash_version_test=true
>>> dictionary_set map foo 2
>>> dictionary_get map foo
2
>>> dictionary__bash_version_test=true
>>> dictionary_set map foo "a b c"
>>> dictionary_get map foo
a b c
>>> dictionary_set map foo "a b c" bar 5
>>> dictionary_get_keys map
bar
foo
>>> dictionary__bash_version_test=true
>>> dictionary_set map foo "a b c" bar 5
>>> dictionary_get_keys map | sort -u
bar
foo
Usage: dictionary.set dictionary_name key value
>>> dictionary_set map foo 2
>>> echo ${dictionary__store_map[foo]}
2
>>> dictionary_set map foo "a b c" bar 5
>>> echo ${dictionary__store_map[foo]}
>>> echo ${dictionary__store_map[bar]}
a b c
5
>>> dictionary_set map foo "a b c" bar; echo $?
1
>>> dictionary__bash_version_test=true
>>> dictionary_set map foo 2
>>> echo $dictionary__store_map_foo
2
>>> dictionary__bash_version_test=true
>>> dictionary_set map foo "a b c"
>>> echo $dictionary__store_map_foo
a b c
The doc_test module implements function and module level testing via "doc strings".
Tests can be run by invoking doc_test.sh file1 folder1 file2 ...
.
--help|-h Print help message.
--side-by-side Print diff of failing tests side by side.
--no-check-namespace Do not warn about unprefixed definitions.
--no-check-undocumented Do not warn about undocumented functions.
--use-nounset Accessing undefined variables produces error.
--verbose|-v Be more verbose
[verbose:doc_test.sh:330] arguments:[PASS]
[verbose:doc_test.sh:330] arguments_get_flag:[PASS]
[verbose:doc_test.sh:330] arguments_get_keyword:[PASS]
[verbose:doc_test.sh:330] arguments_get_parameter:[PASS]
[verbose:doc_test.sh:330] arguments_get_positional:[PASS]
[verbose:doc_test.sh:330] arguments_set:[PASS]
[info:doc_test.sh:590] arguments - passed 6/6 tests in 918 ms
[info:doc_test.sh:643] Total: passed 1/1 modules in 941 ms
A doc string can be defined for a function by defining a variable named
__doc__
at the function scope.
On the module level, the variable name should be <module_name>__doc__
(e.g. arguments__doc__
for the example above).
Note: The doc string needs to be defined with single quotes.
Code contained in a module level variable named
<module_name>__doc_test_setup__
will be run once before all the Tests of
a module are run. This is usefull for defining mockup functions/data
that can be used throughout all tests.
Tests are delimited by blank lines:
>>> echo bar
bar
>>> echo $(( 1 + 2 ))
3
But can also occur right after another:
>>> echo foo
foo
>>> echo bar
bar
Single quotes can be escaped like so:
>>> echo '$foos'
$foos
Or so
>>> echo '$foos'
$foos
Some text in between.
Multiline output
>>> local i
>>> for i in 1 2; do
>>> echo $i;
>>> done
1
2
Ellipsis support
>>> local i
>>> for i in 1 2 3 4 5; do
>>> echo $i;
>>> done
+doc_test_ellipsis
1
2
...
Ellipsis are non greedy
>>> local i
>>> for i in 1 2 3 4 5; do
>>> echo $i;
>>> done
+doc_test_ellipsis
1
...
4
5
Each testcase has its own scope:
>>> local testing="foo"; echo $testing
foo
>>> [ -z "${testing:-}" ] && echo empty
empty
Syntax error in testcode:
>>> f() {a}
+doc_test_contains
+doc_test_ellipsis
syntax error near unexpected token `{a}
...
>>> local buffer="line 1
>>> line 2"
>>> local got="line 1
>>> line 2"
>>> doc_test_compare_result "$buffer" "$got"; echo $?
0
>>> local buffer="line 1
>>> foo"
>>> local got="line 1
>>> line 2"
>>> doc_test_compare_result "$buffer" "$got"; echo $?
1
>>> local buffer="+doc_test_contains
>>> line
>>> line"
>>> local got="line 1
>>> line 2"
>>> doc_test_compare_result "$buffer" "$got"; echo $?
0
>>> local buffer="+doc_test_contains
>>> line
>>> foo"
>>> local got="line 1
>>> line 2"
>>> doc_test_compare_result "$buffer" "$got"; echo $?
1
>>> local buffer="+doc_test_ellipsis
>>> line
>>> ...
>>> "
>>> local got="line
>>> line 2
>>> "
>>> doc_test_compare_result "$buffer" "$got"; echo $?
0
>>> local buffer="+doc_test_ellipsis
>>> line
>>> ...
>>> line 2
>>> "
>>> local got="line
>>> ignore
>>> ignore
>>> line 2
>>> "
>>> doc_test_compare_result "$buffer" "$got"; echo $?
0
>>> local buffer="+doc_test_ellipsis
>>> line
>>> ...
>>> line 2
>>> "
>>> local got="line
>>> ignore
>>> ignore
>>> line 2
>>> line 3
>>> "
>>> doc_test_compare_result "$buffer" "$got"; echo $?
1
>>> local test_buffer="
>>> echo foo
>>> echo bar
>>> "
>>> local output_buffer="foo
>>> bar"
>>> doc_test_use_side_by_side_output=false
>>> doc_test_module_under_test=core
>>> doc_test_nounset=false
>>> doc_test_eval "$test_buffer" "$output_buffer"
>>> local doc_string="
>>> (test)block
>>> output block
>>> "
>>> _() {
>>> local output_buffer="$2"
>>> echo block:
>>> while read -r line; do
>>> if [ -z "$line" ]; then
>>> echo "empty_line"
>>> else
>>> echo "$line"
>>> fi
>>> done <<< "$output_buffer"
>>> }
>>> doc_test_parse_doc_string "$doc_string" _ "(test)"
block:
output block
>>> local doc_string="
>>> Some text (block 1).
>>>
>>>
>>> Some more text (block 1).
>>> (test)block 2
>>> (test)block 2.2
>>> output block 2
>>> (test)block 3
>>> output block 3
>>>
>>> Even more text (block 4).
>>> "
>>> local i=0
>>> _() {
>>> local test_buffer="$1"
>>> local output_buffer="$2"
>>> local text_buffer="$3"
>>> local line
>>> (( i++ ))
>>> echo "text_buffer (block $i):"
>>> if [ ! -z "$text_buffer" ]; then
>>> while read -r line; do
>>> if [ -z "$line" ]; then
>>> echo "empty_line"
>>> else
>>> echo "$line"
>>> fi
>>> done <<< "$text_buffer"
>>> fi
>>> echo "test_buffer (block $i):"
>>> [ ! -z "$test_buffer" ] && echo "$test_buffer"
>>> echo "output_buffer (block $i):"
>>> [ ! -z "$output_buffer" ] && echo "$output_buffer"
>>> return 0
>>> }
>>> doc_test_parse_doc_string "$doc_string" _ "(test)"
text_buffer (block 1):
Some text (block 1).
empty_line
empty_line
Some more text (block 1).
test_buffer (block 1):
output_buffer (block 1):
text_buffer (block 2):
test_buffer (block 2):
block 2
block 2.2
output_buffer (block 2):
output block 2
text_buffer (block 3):
test_buffer (block 3):
block 3
output_buffer (block 3):
output block 3
text_buffer (block 4):
Even more text (block 4).
test_buffer (block 4):
output_buffer (block 4):
Serves a readme via webserver. Uses Flatdoc.
>>> # TODO write test
>>> echo hans
hans
NOTE: The try block is executed in a subshell, so no outer variables can be assigned.
>>> exceptions.activate
>>> false
+doc_test_ellipsis
Traceback (most recent call first):
...
>>> exceptions_activate
>>> exceptions.try {
>>> false
>>> }; exceptions.catch {
>>> echo caught
>>> }
caught
Exceptions in a subshell:
>>> exceptions_activate
>>> ( false )
+doc_test_ellipsis
Traceback (most recent call first):
...
Traceback (most recent call first):
...
>>> exceptions_activate
>>> exceptions.try {
>>> (false; echo "this should not be printed")
>>> echo "this should not be printed"
>>> }; exceptions.catch {
>>> echo caught
>>> }
+doc_test_ellipsis
caught
Nested exceptions:
>>> exceptions_foo() {
>>> true
>>> exceptions.try {
>>> false
>>> }; exceptions.catch {
>>> echo caught inside foo
>>> }
>>> false # this is cought at top level
>>> echo this should never be printed
>>> }
>>>
>>> exceptions.try {
>>> exceptions_foo
>>> }; exceptions.catch {
>>> echo caught
>>> }
>>>
caught inside foo
caught
Exceptions are implicitely active inside try blocks:
>>> foo() {
>>> echo $1
>>> true
>>> exceptions.try {
>>> false
>>> }; exceptions.catch {
>>> echo caught inside foo
>>> }
>>> false # this is not caught
>>> echo this should never be printed
>>> }
>>>
>>> foo "EXCEPTIONS NOT ACTIVE:"
>>> exceptions_activate
>>> foo "EXCEPTIONS ACTIVE:"
+doc_test_ellipsis
EXCEPTIONS NOT ACTIVE:
caught inside foo
this should never be printed
EXCEPTIONS ACTIVE:
caught inside foo
Traceback (most recent call first):
...
Exceptions inside conditionals:
>>> exceptions_activate
>>> false && echo "should not be printed"
>>> (false) && echo "should not be printed"
>>> exceptions.try {
>>> (
>>> false
>>> echo "should not be printed"
>>> )
>>> }; exceptions.catch {
>>> echo caught
>>> }
caught
Print a caught exception traceback.
>>> exceptions.try {
>>> false
>>> }; exceptions.catch {
>>> echo caught
>>> echo "$exceptions_last_traceback"
>>> }
+doc_test_ellipsis
caught
Traceback (most recent call first):
...
Different syntax variations are possible.
>>> exceptions.try {
>>> ! true
>>> }; exceptions.catch {
>>> echo caught
>>> }
>>> exceptions.try
>>> false
>>> exceptions.catch {
>>> echo caught
>>> }
caught
>>> exceptions.try
>>> false
>>> exceptions.catch
>>> echo caught
caught
>>> exceptions.try {
>>> false
>>> }
>>> exceptions.catch {
>>> echo caught
>>> }
caught
>>> exceptions.try {
>>> false
>>> }
>>> exceptions.catch
>>> {
>>> echo caught
>>> }
caught
>>> set -o errtrace
>>> trap 'echo $foo' ERR
>>> exceptions.activate
>>> trap -p ERR | cut --delimiter "'" --fields 2
>>> exceptions.deactivate
>>> trap -p ERR | cut --delimiter "'" --fields 2
exceptions_error_handler
echo $foo
The available log levels are: error critical warn info debug
The standard loglevel is critical
>>> logging.get_level
>>> logging.get_commands_level
critical
critical
>>> logging.error error-message
>>> logging.critical critical-message
>>> logging.warn warn-message
>>> logging.info info-message
>>> logging.debug debug-message
+doc_test_contains
error-message
critical-message
If the output of commands should be printed, the commands_level needs to be greater than or equal to the log_level.
>>> logging.set_level critical
>>> logging.set_commands_level debug
>>> echo foo
>>> logging.set_level info
>>> logging.set_commands_level info
>>> echo foo
foo
Another logging prefix can be set by overriding "logging_get_prefix".
>>> logging_get_prefix() {
>>> local level=$1
>>> echo "[myprefix - ${level}]"
>>> }
>>> logging.critical foo
[myprefix - critical] foo
"logging.plain" can be used to print at any log level and without the prefix.
>>> logging.set_level critical
>>> logging.set_commands_level debug
>>> logging.plain foo
foo
"logging.cat" can be used to print files (e.g "logging.cat < file.txt") or heredocs. Like "logging.plain", it also prints at any log level and without the prefix.
>>> echo foo | logging.cat
foo
>>> logging.set_level info
>>> logging.set_commands_level debug
>>> logging.debug "not shown"
>>> echo "not shown"
>>> logging.plain "shown"
shown
>>> local test_file="$(mktemp)"
>>> logging.plain "test_file:" >"$test_file"
>>> logging_set_file_descriptors ""
>>> logging.cat "$test_file"
>>> rm "$test_file"
test_file:
>>> local test_file="$(mktemp)"
>>> logging_set_file_descriptors "$test_file"
>>> logging_set_file_descriptors ""
>>> echo "test_file:" >"$test_file"
>>> logging.cat "$test_file"
>>> rm "$test_file"
test_file:
>>> local test_file="$(mktemp)"
>>> logging.plain "test_file:" >"$test_file"
>>> logging_set_file_descriptors "$test_file" --logging=tee
>>> logging.plain foo
>>> logging_set_file_descriptors ""
>>> logging.cat "$test_file"
>>> rm "$test_file"
foo
test_file:
foo
>>> local test_file="$(mktemp)"
>>> logging.plain "test_file:" >"$test_file"
>>> logging_set_file_descriptors "$test_file" --logging=off --commands=file
>>> logging.plain not shown
>>> echo foo
>>> logging_set_file_descriptors ""
>>> logging.cat "$test_file"
>>> rm "$test_file"
test_file:
foo
>>> local test_file="$(mktemp)"
>>> logging.plain "test_file:" >"$test_file"
>>> logging_set_file_descriptors "$test_file" --logging=off
>>> logging.plain not shown
>>> echo foo
>>> logging_set_file_descriptors ""
>>> logging.cat "$test_file"
>>> rm "$test_file"
foo
test_file:
>>> local test_file="$(mktemp)"
>>> logging.plain "test_file:" >"$test_file"
>>> logging_set_file_descriptors "$test_file" --commands=tee
>>> logging.plain logging
>>> echo echo
>>> logging_set_file_descriptors ""
>>> logging.cat "$test_file"
>>> rm "$test_file"
logging
echo
test_file:
echo
>>> local test_file="$(mktemp)"
>>> logging.plain "test_file:" >"$test_file"
>>> logging_set_file_descriptors "$test_file" --commands=file
>>> logging.plain logging
>>> echo echo
>>> logging_set_file_descriptors ""
>>> logging.cat "$test_file"
>>> rm "$test_file"
logging
test_file:
echo
>>> local test_file="$(mktemp)"
>>> logging.plain "test_file:" >"$test_file"
>>> logging_set_file_descriptors "$test_file" --logging=file --commands=file
>>> logging.plain logging
>>> echo echo
>>> logging_set_file_descriptors ""
>>> logging.cat "$test_file"
>>> rm "$test_file"
test_file:
logging
echo
>>> local test_file="$(mktemp)"
>>> logging.plain "test_file:" >"$test_file"
>>> logging_set_file_descriptors "$test_file" --logging=file --commands=file
>>> logging.plain logging
>>> echo echo
>>> logging_set_file_descriptors ""
>>> logging.cat "$test_file"
>>> rm "$test_file"
test_file:
logging
echo
>>> local test_file="$(mktemp)"
>>> logging.plain "test_file:" >"$test_file"
>>> logging_set_file_descriptors "$test_file" --logging=file --commands=tee
>>> logging.plain logging
>>> echo echo
>>> logging_set_file_descriptors ""
>>> logging.cat "$test_file"
>>> rm "$test_file"
echo
test_file:
logging
echo
>>> local test_file="$(mktemp)"
>>> logging.plain "test_file:" >"$test_file"
>>> logging_set_file_descriptors "$test_file" --logging=file --commands=off
>>> logging.plain logging
>>> echo echo
>>> logging_set_file_descriptors ""
>>> logging.cat "$test_file"
>>> rm "$test_file"
test_file:
logging
>>> local test_file="$(mktemp)"
>>> logging.plain "test_file:" >"$test_file"
>>> logging_set_file_descriptors "$test_file" --logging=tee --commands=tee
>>> logging.plain logging
>>> echo echo
>>> logging_set_file_descriptors ""
>>> logging.cat "$test_file"
>>> rm "$test_file"
logging
echo
test_file:
logging
echo
Test exit handler
>>> local test_file fifo
>>> test_file="$(mktemp)"
>>> fifo=$(logging_set_file_descriptors "$test_file" --commands=tee; \
>>> echo $logging_tee_fifo)
>>> [ -p "$fifo" ] || echo fifo deleted
>>> rm "$test_file"
fifo deleted
>>> logging.set_commands_level info
>>> logging.set_level info
>>> echo $logging_level
>>> echo $logging_commands_level
3
3
>>> local test_file="$(mktemp)"
>>> logging.plain "test_file:" >"$test_file"
>>> logging.set_log_file "$test_file"
>>> logging.plain logging
>>> logging.set_log_file "$test_file"
>>> echo echo
>>> logging.set_log_file ""
>>> logging.cat "$test_file"
>>> rm "$test_file"
logging
echo
test_file:
logging
echo
>>> logging.set_commands_level debug
>>> logging.set_level debug
>>> local test_file="$(mktemp)"
>>> logging.plain "test_file:" >"$test_file"
>>> logging.set_log_file "$test_file"
>>> logging.plain 1
>>> logging.set_log_file ""
>>> logging.set_log_file "$test_file"
>>> logging.plain 2
>>> logging.set_log_file ""
>>> logging.cat "$test_file"
>>> rm "$test_file"
1
2
test_file:
1
2
This module provides variables for printing colorful and unicode glyphs. The Terminal features are detected automatically but can also be enabled/disabled manually (see ui.enable_color and ui.enable_unicode_glyphs).
Disables color output explicitly.
>>> ui.enable_color
>>> ui.disable_color
>>> echo -E "$ui_color_red" red "$ui_color_default"
red
Disables unicode glyphs explicitly.
>>> ui.enable_unicode_glyphs
>>> ui.disable_unicode_glyphs
>>> echo -E "$ui_powerline_ok"
+
Enables color output explicitly.
>>> ui.disable_color
>>> ui.enable_color
>>> echo -E $ui_color_red red $ui_color_default
�[0;31m red �[0m
Enables unicode glyphs explicitly.
>>> ui.disable_unicode_glyphs
>>> ui.enable_unicode_glyphs
>>> echo -E "$ui_powerline_ok"
✔
This function check if all given dependencies are present.
>>> utils_dependency_check mkdir ls; echo $?
0
>>> utils_dependency_check mkdir __not_existing__ 1>/dev/null; echo $?
2
>>> utils_dependency_check __not_existing__ 1>/dev/null; echo $?
2
>>> utils_dependency_check "ls __not_existing__"; echo $?
__not_existing__
2
This function check if all given libraries can be found.
>>> utils_dependency_check_shared_library libc.so; echo $?
0
>>> utils_dependency_check_shared_library libc.so __not_existing__ 1>/dev/null; echo $?
2
>>> utils_dependency_check_shared_library __not_existing__ 1>/dev/null; echo $?
2
This function check if all given shared libraries can be found.
>>> utils_dependency_check_shared_library libc.so; echo $?
0
>>> utils_dependency_check_shared_library libc.so __not_existing__ 1>/dev/null; echo $?
2
>>> utils_dependency_check_shared_library __not_existing__ 1>/dev/null; echo $?
2
>>> utils_find_block_device "boot_partition"
/dev/sdb1
>>> utils_find_block_device "boot_partition" /dev/sda
/dev/sda2
>>> utils_find_block_device "discoverable by blkid"
/dev/sda2
>>> utils_find_block_device "_partition"
/dev/sdb1 /dev/sdb2
>>> utils_find_block_device "not matching anything" || echo not found
not found
>>> utils_find_block_device "" || echo not found
not found