Bang.sh API

Core

b.set (varname, varvalue)

Sets a globally scoped variable using Registry Pattern

Arguments

varname the name of the variable
varvalue the value for the variable

Source Code

function b.set () {
    local index="$1";
    shift;
    _BANG_REGISTRY["$index"]="$*"
}

b.get (varname)

Gets a globally scoped variable

Arguments

varname the name of the variable

Source Code

function b.get () {
    echo "${_BANG_REGISTRY[$1]}"
}

b.is_set? (varname)

Returns whether a variable is set or not

Arguments

varname the name of the variable

Source Code

function b.is_set? () {
    key_exists? "$1" _BANG_REGISTRY;
    return $?
}

b.unset (varbeginning)

Unset a variable and all the ones that follow its name. For instance: $ b.unset Bang.Test It would unset Bang.Test, Bang.Testing, Bang.Test.Something and so on

Arguments

varbeginning the beginning of the varnames to be unsetted

Source Code

function b.unset () {
    for key in "${!_BANG_REGISTRY[@]}";
    do
        echo "$key" | grep -q "^$1";
        [ $? -eq 0 ] && unset _BANG_REGISTRY["$key"];
    done
}

is_module? (module)

Return whether the argument is a valid module

Arguments

module the name of the module

Source Code

function is_module? () {
    b.module.resolve_path "$1" &>/dev/null;
    return $?
}

in_array? (element, array)

Checks if the element is in the given array name

Arguments

element element to be searched in array
array name of the array variable to search in

Source Code

function in_array? () {
    local element="$1" array="$2";
    test -z "$element" -o -z "$array" && return 1;
    array=$(sanitize_arg "$array");
    local values="$(eval echo \"\${$array[@]}\")";
    element=$(escape_arg "$element");
    echo "$values" | grep -wq "$element";
    return $?
}

key_exists? (key, array)

Checks if the given key exists in the given array name

Arguments

key key to check
array name of the array variable to be checked

Source Code

function key_exists? () {
    local key="$1" array="$2";
    test -z "$key" -o -z "$array" && return 1;
    array=$(sanitize_arg "$array");
    echo "$(eval echo \"\${!$array[@]}\")" | grep -wq "$(escape_arg $key)";
    return $?
}

escape_arg (arg)

Returns the escaped arg (turns -- into \--)

Arguments

arg Argument to be escaped

Source Code

function escape_arg () {
    local arg="$@";
    [ -z "$arg" ] && read arg;
    if [ "${arg:0:1}" == '-' ]; then
        arg="\\$arg";
    fi;
    echo -e "$arg"
}

sanitize_arg (arg)

Returns the sinitized argument

Arguments

arg Argument to be sinitized

Source Code

function sanitize_arg () {
    local arg="$1";
    [ -z "$arg" ] && read arg;
    arg=$(echo "$arg" | sed 's/[;&]//g' | sed 's/^ *//g ; s/ *$//g');
    echo "$arg"
}

is_function? (funcname)

Checks if a function exists

Arguments

funcname Name of function to be checked

Source Code

function is_function? () {
    declare -f "$1" &>/dev/null && return 0;
    return 1
}

print_e ([text ...])

Print to the stderr

Arguments

[text ...] Text to be printed in stderr

Source Code

function print_e () {
    echo -e "$*" 1>&2
}

b.abort ([msg ...])

Raises an error an exit the code

Arguments

[msg ...] Message of the error to be raised

Source Code

function b.abort () {
    print_e "The program was aborted due to an error:\n\n\t$*";
    exit 2
}

b.raise (exception)

Raises an exception that can be cautch by catch statement

Arguments

exception a string containing the name of the exception

Source Code

function b.raise () {
    local exception="$1";
    shift;
    if echo "${FUNCNAME[@]}" | grep -q 'b.try.do'; then
        b.set "Bang.Exception.Name" "$exception";
        b.set "Bang.Exception.Msg" "$*";
    else
        b.abort "Uncautch exception $exception: $*";
    fi
}

b.raised_message ()

Returns the last raised message by b.raise

Source Code

function b.raised_message () {
    echo $(b.get "Bang.Exception.Msg")
}

b.try.do (funcname)

Simple implementation of the try statement which exists in other languages

Arguments

funcname a string containing the name of the function that can raises an exception

Source Code

function b.try.do () {
    local funcname="$1";
    if is_function? "$funcname"; then
        shift;
        $funcname "$@";
    fi
}

b.catch (exception, funcname)

Catches an exception fired by b.raise and executes a function

Arguments

exception a string containing the exception fired by b.raise
funcname a string containing the name of the function to handle exception

Source Code

function b.catch () {
    if [ "$(b.get Bang.Exception.Name)" = "$1" ]; then
        is_function? "$2" && "$2";
    else
        if [ -z "$1" ]; then
            is_function? "$2" && "$2";
        fi;
    fi
}

b.finally (funcname)

Executes this command whether an exception is called or not

Arguments

funcname a string containing the name of the function to be executed

Source Code

function b.finally () {
    b.set "Bang.Exception.Finally" "$1"
}

b.try.end ()

End a try/catch statement

Source Code

function b.try.end () {
    $(b.get "Bang.Exception.Finally");
    b.unset Bang.Exception
}

OptionParser

b.opt.reset ()

Resets this module

Source Code

function b.opt.reset () {
    b.unset "Bang.Opt"
}

b.opt.add_opt (opt, description)

Adds an option with a description to the software

Arguments

opt Option to be added
description Description of th opt

Source Code

function b.opt.add_opt () {
    local opt="Bang.Opt.Opts.$1" description="$2";
    [ -z "$1" ] && return 1;
    if b.opt.is_opt? "$(b.opt.alias2opt $1)"; then
        b.raise OptionAlreadySet "Option '$1' already exists or is an alias and cannot be added again.";
    else
        if b.opt.is_flag? "$1"; then
            b.raise FlagAlreadySet "Flag '$1' already exists and cannot be overriden.";
        else
            b.set "$opt" "$description";
            b.set "Bang.Opt.AllOpts" "$(b.get Bang.Opt.AllOpts) $1";
            return 0;
        fi;
    fi
}

b.opt.add_flag (flag, description)

Adds a flag with a description to the software

Arguments

flag Flag to be added
description Description of the flag

Source Code

function b.opt.add_flag () {
    local flag="Bang.Opt.Flags.$1" description="$2";
    [ -z "$1" ] && return 1;
    if b.opt.is_opt? "$(b.opt.alias2opt $1)"; then
        b.raise OptionAlreadySet;
    else
        if b.opt.is_flag? "$1"; then
            b.raise "Flag '$flag' already exists and cannot be added again.";
        else
            b.set "$flag" "$description";
            b.set "Bang.Opt.AllOpts" "$(b.get Bang.Opt.AllOpts) $1";
            return 0;
        fi;
    fi
}

b.opt.add_alias (opt, [aliases ...])

Adds an alias for an existing option or flag

Arguments

opt Option to be aliased
[aliases ...] Aliases of the option

Source Code

function b.opt.add_alias () {
    local opt="$1" total="$#";
    shift;
    if [ ! -z "$opt" ] && [ $total -gt 1 ]; then
        local sum=0;
        b.opt.is_opt? "$(b.opt.alias2opt $opt)" && sum=$(($sum + 1));
        b.opt.is_flag? "$opt" && sum=$(($sum + 1));
        [ $sum -ne 1 ] && b.raise OptionDoesNotExist "Option '$opt' does not exist, no alias can be added.";
        local i=1;
        while [ $i -lt $total ]; do
            local alias="Bang.Opt.Alias.$1";
            shift;
            b.set "$alias" "$opt";
            b.set "Bang.Opt.AliasFor.$opt" "$alias $(b.get Bang.Opt.AliasFor.$opt)";
            let i++;
        done;
        return 0;
    fi;
    return 1
}

b.opt.aliases_for ()

Source Code

function b.opt.aliases_for () {
    local opt=$1 aliases=();
    for aliasname in $(b.get "Bang.Opt.AliasFor.$opt");
    do
        aliases+=("${aliasname#Bang.Opt.Alias.}");
    done;
    echo "${aliases[@]}"
}

b.opt.required_args ([opts ...])

Sets the required args of the command line

Arguments

[opts ...] A set of options that are required

Source Code

function b.opt.required_args () {
    local i="";
    if [ $# -gt 0 ]; then
        for i in $(seq 1 $#);
        do
            local opt=$(eval "echo \$$i");
            opt=$(b.opt.alias2opt "$opt");
            if b.opt.is_opt? "$opt" || b.opt.is_flag? "$opt"; then
                b.set "Bang.Opt.Required" "$(b.get Bang.Opt.Required) $opt";
            fi;
        done;
        return 0;
    fi;
    return 1
}

b.opt.has_flag? (flag)

Checks if the flag is set

Arguments

flag Flag to be checked

Source Code

function b.opt.has_flag? () {
    local reqopt="$(b.opt.alias2opt $1)";
    echo $(b.get "Bang.Opt.ParsedFlag") | grep -q "^$reqopt\b\| $reqopt\b"
}

b.opt.get_opt (opt)

Returns the value of the option

Arguments

opt Opt which value is to be returned

Source Code

function b.opt.get_opt () {
    echo $(b.get "Bang.Opt.ParsedArg.$1");
    b.is_set? "Bang.Opt.ParsedArg.$1";
    return $?
}

b.opt.show_usage ()

Shows usage informations

Source Code

function b.opt.show_usage () {
    echo -e "\nShowing usage:\n";
    local opt="";
    for opt in $(b.get "Bang.Opt.AllOpts");
    do
        local fullopt="$opt" alias="";
        for aliasname in $(b.opt.aliases_for $opt);
        do
            fullopt="$fullopt|$aliasname";
        done;
        b.opt.is_opt? "$opt" && fullopt="$fullopt \t\t";
        b.opt.is_required? "$opt" && fullopt="$fullopt (Required)";
        local desc="";
        b.opt.is_opt? "$opt" && desc="$(b.get Bang.Opt.Opts.$opt)";
        b.opt.is_flag? "$opt" && desc="$(b.get Bang.Opt.Flags.$opt)";
        [ ! -z "$desc" ] && fullopt="$fullopt\n\t\t$desc\n";
        echo -e "$fullopt";
    done
}

b.opt.init ()

Parses the arguments of command line

Source Code

function b.opt.init () {
    local -i i=1;
    for ((1; $i <= $# ; i++ ))
    do
        local arg=$(eval "echo \$$i");
        arg=$(b.opt.alias2opt $arg);
        if b.opt.is_opt? "$arg"; then
            local ii=$(($i + 1));
            local nextArg=$(eval "echo \$$ii");
            if [ -z "$nextArg" ] || b.opt.is_opt? "$nextArg" || b.opt.is_flag? "$nextArg"; then
                b.raise ArgumentError "Option '$arg' requires an argument.";
            else
                b.set "Bang.Opt.ParsedArg.$arg" "$nextArg";
                let i++;
            fi;
        else
            if b.opt.is_flag? "$arg"; then
                b.set "Bang.Opt.ParsedFlag" "$(b.get Bang.Opt.ParsedFlag) $arg";
            else
                b.raise ArgumentError "Option '$arg' is not a valid option.";
            fi;
        fi;
    done
}

b.opt.check_required_args ()

Checks for required args... if some is missing, raises an error

Source Code

function b.opt.check_required_args () {
    local reqopt="" required_options="$(b.get Bang.Opt.Required)";
    [ -z "$required_options" ] && return 0;
    for reqopt in $required_options;
    do
        is_opt=$(b.is_set? "Bang.Opt.ParsedArg.$reqopt" ; echo $?);
        is_alias=$(b.opt.has_flag? "$reqopt" ; echo $?);
        sum=$(($is_opt + $is_alias));
        if [ $sum -gt 1 ]; then
            b.raise RequiredOptionNotSet "Option '$reqopt' is required and was not specified";
            return 1;
        fi;
    done;
    return 0
}

b.opt.alias2opt ()

Translates aliases to real option

Source Code

function b.opt.alias2opt () {
    local arg="$1";
    if b.is_set? "Bang.Opt.Alias.$arg"; then
        echo $(b.get "Bang.Opt.Alias.$arg");
        return 0;
    fi;
    echo "$arg";
    return 1
}

b.opt.is_opt? (arg)

Checks if the argument is an option

Arguments

arg Argument to be checked

Source Code

function b.opt.is_opt? () {
    local arg="$1" opt="$(b.opt.alias2opt $1)";
    b.is_set? "Bang.Opt.Opts.$opt";
    return $?
}

b.opt.is_flag? (arg)

Checks if the argument is a flag

Arguments

arg Argument to be checked

Source Code

function b.opt.is_flag? () {
    local opt="$(b.opt.alias2opt $1)";
    b.is_set? "Bang.Opt.Flags.$opt";
    return $?
}

b.opt.is_required? ()

Source Code

function b.opt.is_required? () {
    echo $(b.get Bang.Opt.Required) | grep -q "^$1\b\| $1\b";
    return $?
}

Path

b.path.expand (path)

Expands a given path to its full path. If it has synlinks, it will be converted to the realpath. If it is a file, the path will also be expanded to realpath It does not return a trailling '/' for directories

Arguments

path a path to be expanded

Source Code

function b.path.expand () {
    local _DIR="$1" _FILE="";
    if [ -f "$1" ]; then
        _DIR="${1%/*}/";
        _FILE="${1/$_DIR/}";
    fi;
    ( ( if cd -P "$_DIR" &>/dev/null; then
        _REALPATH="$PWD/$_FILE";
        echo "${_REALPATH%/}";
    fi ) )
}

b.path.dir? (path)

Returns true if the passed path is a directory, false otherwise

Arguments

path the path to be checked

Source Code

function b.path.dir? () {
    test -d "$1"
}

b.path.file? (path)

Returns true if the passed path is a file, false otherwise

Arguments

path the path to be checked

Source Code

function b.path.file? () {
    test -f "$1"
}

b.path.block? (path)

Returns whether the path is a file

Arguments

path the path to be checked

Source Code

function b.path.block? () {
    test -b "$1"
}

b.path.readable? (path)

Returns whether the path is readable

Arguments

path the path to be checked

Source Code

function b.path.readable? () {
    test -r "$1"
}

b.path.writable? (path)

Returns whether the path is writable

Arguments

path the path to be checked

Source Code

function b.path.writable? () {
    test -w "$1"
}

b.path.older? (path, another_path)

Returns whether the path is older than another path

Arguments

path the path to be checked
another_path the path to be checked against

Source Code

function b.path.older? () {
    test "$1" -ot "$2"
}

b.path.newer? (path, another_path)

Returns whether the path is newer than another path

Arguments

path the path to be checked
another_path the path to be checked against

Source Code

function b.path.newer? () {
    test "$1" -nt "$2"
}

String

b.str.replace (varname, search, replace)

Replaces a given string to another in a string variable

Arguments

varname the name of the variable
search the string to be searched
replace the string to be replaced by

Source Code

function b.str.replace () {
    local varname="$(eval echo \$$1)" search="$2" replace="$3";
    echo ${varname/$search/$replace}
}

b.str.part (varname, offset, length)

Returns a part of the string. If no length is given, it will return until the last char of the string. Negative lengths are relative from the back of the string

Arguments

varname the name of the variable
offset the starting offset
length the length of chars to include

Source Code

function b.str.part () {
    local varname="$(eval echo \$$1)";
    if [ $# -eq 3 ]; then
        echo ${varname: $2:$3};
    else
        if [ $# -eq 2 ]; then
            echo ${varname: $2};
        else
            b.raise InvalidArgumentsException;
        fi;
    fi
}

b.str.trim (string)

Trims spaces and tabs from the beginning and at the end string

Arguments

string string to be trimmed

Source Code

function b.str.trim () {
    local arg="$*";
    [ -z "$arg" ] && read arg;
    echo "$arg" | sed -E 's/^[ \t]*//g ; s/[ \t]*$//g'
}

UnitTest

b.unittest.add_test_case (testcase, description)

Unit Test Framework Adds test cases to be executed

Arguments

testcase Function with assertions
description Description of the testcase

Source Code

function b.unittest.add_test_case () {
    if is_function? "$1"; then
        _BANG_TESTFUNCS+=($1);
        shift;
        _BANG_TESTDESCS+=("$@");
    fi
}

b.unittest.run_tests ()

Runs all added test cases

Source Code

function b.unittest.run_tests () {
    local i=0;
    echo;
    while [ $i -lt ${#_BANG_TESTFUNCS[@]} ]; do
        is_function? b.unittest.setup && b.unittest.setup;
        echo "Running testcase '${_BANG_TESTFUNCS[$i]}' (${_BANG_TESTDESCS[$i]})";
        echo;
        ${_BANG_TESTFUNCS[$i]};
        let i++;
        is_function? b.unittest.teardown && b.unittest.teardown;
    done;
    echo "$i tests executed (Assertions: $_BANG_ASSERTIONS_PASSED passed / $_BANG_ASSERTIONS_FAILED failed)"
}

b.unittest.autorun_tests ()

Autoadd and run all test functions

Source Code

function b.unittest.autorun_tests () {
    for func in $(declare -f | grep '^b\.test\.' | sed 's/ ().*$//');
    do
        b.unittest.add_test_case "$func";
    done;
    b.unittest.run_tests
}

b.unittest.assert_success (return code)

Asserts a function exit code is zero

Arguments

return code return code of the command

Source Code

function b.unittest.assert_success () {
    if [ $1 -gt 0 ]; then
        print_e "'$@'... FAIL";
        print_e "Expected TRUE, but exit code is NOT 0";
        let _BANG_ASSERTIONS_FAILED++;
        return 1;
    fi;
    let _BANG_ASSERTIONS_PASSED++;
    return 0
}

b.unittest.assert_error (funcname)

Asserts a functoin exit code is 1

Arguments

funcname Name of the function

Source Code

function b.unittest.assert_error () {
    if [ $1 -eq 0 ]; then
        print_e "'$@'... FAIL";
        print_e "Expected FALSE, but exit code is 0";
        let _BANG_ASSERTIONS_FAILED++;
        return 1;
    fi;
    let _BANG_ASSERTIONS_PASSED++;
    return 0
}

b.unittest.assert_equal (reqvalue, funcname)

Asserts a function output is the same as required

Arguments

reqvalue Value to be equals to the output
funcname Name of the function which result is to be tested

Source Code

function b.unittest.assert_equal () {
    local val="$1";
    shift;
    local result="$1";
    if [ "$val" != "$result" ]; then
        print_e "'$@' equals to '$val'... FAIL";
        print_e "Expected '$val', but it was returned '$result'";
        let _BANG_ASSERTIONS_FAILED++;
        return 1;
    fi;
    let _BANG_ASSERTIONS_PASSED++;
    return 0
}

b.unittest.assert_raise (funcname, exception)

Asserts a function will raise a given exception

Arguments

funcname a string containing the name of the function which will raise an exception
exception a string containing the exception which should be raise

Source Code

function b.unittest.assert_raise () {
    local fired=0;
    function catch_exception () 
    { 
        fired=1
    };
    b.try.do "$1";
    b.catch "$2" catch_exception;
    b.try.end;
    if [ $fired -eq 1 ]; then
        let _BANG_ASSERTIONS_PASSED++;
    else
        let _BANG_ASSERTIONS_FAILED++;
        print_e "'$1' has not raised '$2' as expected...";
    fi;
    unset -f catch_exception
}

b.unittest.double.do (func1, func2)

Do a double for a function, replacing it codes for the other functions' code

Arguments

func1 a string containing the name of the function to be replaced
func2 a string containing the name of the function which will replace func1

Source Code

function b.unittest.double.do () {
    if is_function? "$1" && is_function? "$2"; then
        actualFunc=$(declare -f "$1" | sed '1d;2d;$d');
        func=$(declare -f "$2" | sed '1d;2d;$d');
        func_name=$(echo $1 | sed 's/\./_/g');
        _BANG_MOCKS+=(["$func_name"]="$actualFunc");
        eval "function $1 () {
      $func
    }";
    fi
}

b.unittest.double.undo (func)

Undo the double for the function

Arguments

func the string containing the name of the function

Source Code

function b.unittest.double.undo () {
    func_name=$(echo $1 | sed 's/\./_/g');
    if key_exists? "$func_name" "_BANG_MOCKS"; then
        eval "function $1 () {
      ${_BANG_MOCKS["$func_name"]}
    }";
    fi
}