BAUX is a bash auxiliary library for writing script.
- ๐จ Features
- ๐ Prerequisite
- ๐ Installation
- ๐ Usage
- ๐บ Contributing
- ๐ฆ Authors
- ๐ License
- Helper: Basic script writing helper functions, such as getting script's name, version and help message, importing other script once, warning or exit when get a wrong status. (
baux.sh
) - Assertion: Functions for writing reliable APIs, ensuring the pre- or post-condition. (
ensure.sh
) - Utility: Useful utility functions for getting options, reading a simple config file, printing message with color and so on. (
utils.sh
) - Debugging: Simple functions for logging (
log.sh
) and print callstack when failed (trace.sh
). - Testing: Functions for check a variable (
var.sh
) and writing unit tests (test.sh
). - Exception: (Not yet finished)
try()
,catch()
,throw()
.
- Array: Functions for array manipulation. (
array.sh
)- Data structure: stack, queue.
- Sort and search:
sort()
,bsearch()
.
- Pattern: POSIX compatible characters patterns and other common regex. (
pattern.sh
)- Pattern match: IP, URL, tele-number, etc.
is
pattern check.
You can get this program with git
:
$ git clone https://github.com/ishbguy/baux
lib
โโโ array.sh # array manipulate functions
โโโ baux.sh # basic helper functions
โโโ pattern.sh # POSIX compatible characters patterns and other common regex
โโโ ensure.sh # assertion functions
โโโ except.sh # not yet finished
โโโ log.sh # simple logging
โโโ test.sh # unit test functions
โโโ trace.sh # simple callstack function
โโโ utils.sh # useful tools
โโโ var.sh # checking variables
except.sh
|-----------------------------------------------+
V |
array.sh test.sh |
| |-----------------------------------|
V V V
var.sh utils.sh pattern.sh log.sh trace.sh
| | | | |
+-----------+-----------+-----------+-----------+
V
baux.sh <-> ensure.sh
As the Library Dependence Diagram above, you can easily source one of the library file to include the functions you need, for example:
# in your script
source /path/to/baux/lib/baux.sh
[[ -e $file ]] || die "$file not exist."
# your script
source /path/to/baux/lib/baux.sh
[[ -e $opt_file ]] || warn "Just warn you that $opt_file does not exsit"
[[ -e $need_file ]] || die "$need_file not found! This will exit with $BAUX_EXIT_CODE"
echo "Can not be here."
PS: Though warn
runs successfully, it does return ((++BAUX_EXIT_CODE))
in the end, so warn
returns an none-zero when it finished!
#! /usr/bin/env bash
# your script
source /path/to/baux/lib/baux.sh
echo "The script name is $(proname)" # will print the script name
VERSION="v0.0.1" # need to define VERSION first, or version will warn
echo "The script version is $(version)" # will print the script version
HELP="This is a help message." # need to define HELP first, or usage will warn
usage # print help message
PS: usage
call version
first to print version message, then print help message, so, both VERSION
and HELP
should be predefined when call usage
.
baux.sh
includes an import
function to ensure source a file for only one time.
source /path/to/baux/lib/baux.sh
import /path/to/your/lib.sh # this will import once
import /path/to/your/lib.sh # OK, but will not import lib.sh again
cmd_from_lib_sh
import /file/not/exsit.sh # this will fail, and make you script die
echo "Can not be here!"
Assertion functions are useful for writing APIs, and they can be sorted in 3 catogories: general, explicit and implicit assertion.
ensure
can make an assertion with a given expression, if the expression is true, it will continue to run the following commands, or it will die and print a given message.
source /path/to/baux/lib/ensure.sh
# the first arg is expression, the second arg is error message, which is optional
NUM=1
ensure "$NUM == 1" "$NUM must be equal 1" # OK
ensure "2 -gt $NUM" "2 must greater than $NUM" # OK
ensure "$NUM == 1.0" "$NUM must be 1.0" # this will die, for $NUM act as string '1' not '1.0'
The expression given to ensure
will be eval [[ $expr ]]
inside ensure
.
ensure_not_empty
can test all given args wheather they are not empty, if anyone of them is empty, it will die.
source /path/to/baux/lib/ensure.sh
one=1
two=2
empty=
ensure_not_empty "$one" "$two" # OK
ensure_not_empty "$empty" # will die
echo "Can not be here."
It is a best practice to wrap each arg with double qoute.
ensure_is "$1" "$2"
is equivalent to ensure "$1 == $2".
source /path/to/baux/lib/ensure.sh
one=1
ensure_is "$one" "1" "$one is not 1" # OK
ensure_is "$one" "1.0" "$one is not 1" # will die
echo "Can not be here."
ensure_isnt "$1" "$2"
is equivalent to ensure "$1 != $2".
source /path/to/baux/lib/ensure.sh
one=1
ensure_isnt "$one" "1.0" "$one is not 1" # OK
ensure_isnt "$one" "1" "$one is not 1" # will die
echo "Can not be here."
ensure_like "$1" "$2"
is equivalent to ensure "$1 =~ $2"
source /path/to/baux/lib/ensure.sh
str="This is a test"
ensure_like "$str" "a" "$str does not like a" # OK
ensure_like "$str" "test" "$str does not like test" # OK
ensure_like "$str" "check" "$str does not like check" # will die
echo "Can not be here."
ensure_unlike "$1" "$2"
is equivalent to ensure "! $1 =~ $2"
source /path/to/baux/lib/ensure.sh
str="This is a test"
ensure_unlike "$str" "TEST" "$str like TEST" # OK
ensure_unlike "$str" "IS" "$str like IS" # OK
ensure_unlike "$str" "test" "$str like test" # will die
echo "Can not be here."
The BAUX_ENSURE_DEBUG
variable act as a switch to turn on or off the assertion, its default value is 1
, which means turn on assertion, if you want to turn off, you can set BAUX_ENSURE_DEBUG
to 0
before sourcing the ensure.sh
.
source /path/to/baux/lib/utils.sh
# need to declare two associative arrays
# one for options and one for arguments
declare -A opts args
# The first arg is array NAME for options
# The second arg is array NAME for arguments
# The third arg is the options string, a letter for an option,
# letter follow with ':' means a option argument
# The remain args are needed to be parsed
getoptions opts args "n:vh" "$@"
# after getoptions, need to correct the option index
shift $((OPTIND - 1))
# now you can check options and arguments
[[ ${opts[h]} -eq 1 ]] && echo "Option 'h' invoke"
[[ ${opts[v]} -eq 1 ]] && echo "Option 'v' invoke"
[[ ${opts[n]} -eq 1 ]] && echo "Option 'n' invoke, argument is ${args[n]}"
# an invoked option will be assigned with 1
# an invoked option with an argument, the argument value will be stored
source /path/to/baux/lib/utils.sh
# need to declare an associative array for storing config value
declare -A CONFIGS
# config name and value are seperated with '='
# strings follow '#' means comment
# each line allows one name-value pair
# leading and tailing spaces is allowed
# spaces on both sides of '=' is allowed, too
echo "NAME=ishbguy" >>my.config
echo "EMAIL=ishbguy@hotmail.com" >>my.config
read_config CONFIGS my.config
# you will notice that all config name will convert to lower case
echo "my name is ${CONFIGS[name]}"
echo "my email is ${CONFIGS[email]}"
read_config CONFIGS file-not-exsit # will not fail, just return 1
source /path/to/baux/lib/utils.sh
cecho red "This message will print in red" # color can be: black, red, green
# yellow, blue, magenta, cyan, white
realdir /path/to/script # similar to realpath, this will print /path/to
realdir /p1/script1 /p2/script2 # will print /p1 /p2
check_tool sed awk realpath # check needed tools in PATH
check_tool tools-not-exsit # will die
BAUX
has a simple log
function which can accept a log level and message string args, then print the log mesages to stdout
or a specified log file.
BAUX
also has a simple callstack
function to print out the call stack when encountering error.
source /path/to/baux/lib/log.sh
# log has 5 log level and priority from low to high is:
#
# debug < info < warn < error < fatal < panic < quiet
#
# default log output level is debug, means that the log priority higher than
# debug will be printed.
log debug "This is a log test" # this will print a log message to stdout
BAUX_LOG_OUTPUT_LEVEL=info # set log output level to info
log debug "a debug message" # will not print
log info "a info message" # will print into stdout
# set a log output file, default is empty which will print into stdout
# if test.log is not exsit, log will create it
BAUX_LOG_OUTPUT_FILE=test.log
log info "write into a log file" # this will write into test.log
source /path/to/baux/lib/trace.sh
# callstack need a start index of the function stack array
# if index is 0, which will print also the callstack 0, if index is 1,
# which will not print callstack line, and start to print from function
# three.
one() {
two
}
two() {
three
}
three() {
callstack 0
}
one
Run the above test script, will print the callstack to stdout like:
+ callstack 0 [./test.sh:16:three]
+ three [./test.sh:13:two]
+ two [./test.sh:10:one]
+ one [./test.sh:19:main]
source /path/to/baux/lib/var.sh
declare -a var
type_array var && echo "this is an index array"
type_map var && echo "this is an associative array"
type_int var && echo "this is an integer var"
type_func var && echo "this is a function name"
type_ref var && echo "this is a var reference"
type_export var && echo "this is an exported var"
type_lower var && echo "var's value has lower case attribute"
type_upper var && echo "var's value has upper case attribute"
BAUX
's test functions are similar to Perl
's Test::More
module.
source /path/to/baux/lib/test.sh
# ok $expr test is equivalent to [[ $expr ]]
ok '1 == 1' 'Test equal' # pass
ok '1 != 0' 'Test not equal' # pass
# is $a $b test is equivalent to [[ $a == $b ]]
is 1 1 "Test equal" # pass
is 1 0 "Test not equal" # fail
# isnt $a $b test is equivalent to [[ $a != $b ]]
isnt 1 1 "Test equal" # fail
isnt 1 0 "Test not equal" # pass
# like $a $b test is equivalent to [[ $a =~ $b ]]
like "apple" "app" "Test like" # pass
like "apple" "App" "Test not like" # fail
# unlike $a $b test is equivalent to [[ ! $a =~ $b ]]
unlike "apple" "app" "Test like" # fail
unlike "apple" "App" "Test not like" # pass
# each test status will print when that test finish
# finally, report a summary with the numbers of:
# total, pass, fail and skip
summary
source /path/to/baux/lib/test.sh
# run_ok like ok, but run_ok provide $status, $output inside to test the
# exit status and cmd output. When use these 2 variables, you need to single
# quote the expr to avoid expanded outside run_ok. And the given cmd is run
# in a subshell like output=$(eval "$cmd" 2>&1), you can check both nomral
# and error message in $output.
run_ok '$status -eq 0' exit 0 # pass
run_ok '$status -eq 1' exit 1 # pass
run_ok '$output =~ "command not found"' cmd_not_found # pass
run_ok '$output =~ "hello world"' echo "hello world" # pass
# you can also do like this for the test statement inside run_ok is equivalent
# to [[ $expr ]]
run_ok '$status -eq 0 && $(echo $output | wc -l) -eq 1' echo "test" # pass
summary
subtest
will group the your tests in a single sub-test, if one of the tests in subtest
fails, the subtest
will fail, and the total and fail counters will increase 1.
source /path/to/baux/lib/test.sh
# format like: subtest "test name" 'tests cmds'
subtest 'subtest PASS' "
is 1 1 'test equal'
isnt 1 0 'test not equal'
"
summary
source /path/to/baux/lib/test.sh
is 1 1 # pass
skip
isnt 1 1 # this will skip
isnt 0 1 # this will run and pass
summary
You can customize test's total, pass, fail, skip prompt strings and colors.
source /path/to/baux/lib/test.sh
# these are default prompt strings
BAUX_TEST_PROMPTS[TOTAL]="TOTAL"
BAUX_TEST_PROMPTS[PASS]="PASS"
BAUX_TEST_PROMPTS[FAIL]="FAIL"
BAUX_TEST_PROMPTS[SKIP]="SKIP"
# these are default colors, you can change color which cecho accept
BAUX_TEST_COLORS[TOTAL]="blue"
BAUX_TEST_COLORS[PASS]="green"
BAUX_TEST_COLORS[FAIL]="red"
BAUX_TEST_COLORS[SKIP]="yellow"
BAUX_TEST_COLORS[EMSG]="red"
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request
Released under the terms of MIT License.