GithubHelp home page GithubHelp logo

niieani / bash-oo-framework Goto Github PK

View Code? Open in Web Editor NEW
5.5K 139.0 249.0 676 KB

Bash Infinity is a modern standard library / framework / boilerplate for Bash

Home Page: https://github.com/niieani/bash-oo-framework/discussions

License: MIT License

Shell 100.00%
bash shell framework oop functional-programming boilerplate standard-library testing error-handling logging

bash-oo-framework's Introduction

Bash Infinity

Build Status Build Status Join the chat at https://gitter.im/niieani/bash-oo-framework

Bash Infinity is a standard library and a boilerplate framework for writing tools using bash. It's modular and lightweight, while managing to implement some concepts from C#, Java or JavaScript into bash. The Infinity Framework is also plug & play: include it at the beginning of your existing script to import any of the individual features such as error handling, and start using other features gradually.

The aim of Bash Infinity is to maximize readability of bash scripts, minimize the amount of code repeat and create a central repository for well-written, and a well-tested standard library for bash.

Bash Infinity transforms the often obfuscated "bash syntax" to a cleaner, more modern syntax.

Disclaimer

Some components are more sturdy than others, and as-it-stands the framework lacks good test coverage (we need your help!).

Due to the above and relatively high code-complexity, we have decided that it will make the most sense to do a rewrite for the next major version 3.0 (see discussion in #45), taking the best parts of the framework, while re-using established tools from bash community.

At this point, I would not recommend starting major projects based on the whole framework. Instead, copy and paste parts you need, ideally those you understand, if you found a particular feature useful.

Compatibility

Not all of the modules work with earlier versions of bash, as I test with bash 4. However, it should be possible (and relatively easy) to port non-working parts to earlier versions.

Quick-start

Single-file release and dynamic loading is not available for v2.0 yet. To load the framework locally, read on.

Main modules

  • automatic error handling with exceptions and visual stack traces (util/exception)
  • named parameters in functions (instead of $1, $2...) (util/namedParameters)
  • passing arrays and maps as parameters (util/variable)
  • try-catch implementation (util/tryCatch)
  • throwing custom exceptions (util/exception)
  • import keyword for clever sourcing of scripts à la require-js (oo-bootstrap)
  • handy aliases for colors and powerline characters to increase readability in the output of your scripts (UI/Color)
  • well-formatted, colorful logging to stderr or custom delegate functions (util/log)
  • unit test library (util/test)
  • standard library for the type system with plenty of useful functions (util/type)
  • operational chains for functional programming in bash (util/type)
  • type system for object-oriented scripting (util/class)

All of the features are modular and it's easy to only import the ones you'd like to use, without importing the rest of the framework. For example, the named parameters or the try-catch modules are self-contained in individual files.

Error handling with exceptions and throw

import util/exception

One of the highlight features is error handling that should work out of the box. If the script generates an error it will break and display a call stack:

example call stack

You may also force an error by throwing your own Exception:

e="The hard disk is not connected properly!" throw

It's useful for debugging, as you'll also get the call stack if you're not sure where the call is coming from.

Exceptions combined with try & catch give you safety without having to run with -o errexit.

If you do something wrong, you'll get a detailed exception backtrace, highlighting the command where it went wrong in the line from the source. The script execution will be halted with the option to continue or break. On the other hand if you expect a part of block to fail, you can wrap it in a try block, and handle the error inside a catch block.

Named parameters in functions

import util/namedParameters

In any programing language, it makes sense to use meaningful names for variables for greater readability. In case of Bash, that means avoiding using positional arguments in functions. Instead of using the unhelpful $1, $2 and so on within functions to access the passed in values, you may write:

testPassingParams() {

    [string] hello
    [string[4]] anArrayWithFourElements
    l=2 [string[]] anotherArrayWithTwo
    [string] anotherSingle
    [reference] table   # references only work in bash >=4.3
    [...rest] anArrayOfVariedSize

    test "$hello" = "$1" && echo correct
    #
    test "${anArrayWithFourElements[0]}" = "$2" && echo correct
    test "${anArrayWithFourElements[1]}" = "$3" && echo correct
    test "${anArrayWithFourElements[2]}" = "$4" && echo correct
    # etc...
    #
    test "${anotherArrayWithTwo[0]}" = "$6" && echo correct
    test "${anotherArrayWithTwo[1]}" = "$7" && echo correct
    #
    test "$anotherSingle" = "$8" && echo correct
    #
    test "${table[test]}" = "works"
    table[inside]="adding a new value"
    #
    # I'm using * just in this example:
    test "${anArrayOfVariedSize[*]}" = "${*:10}" && echo correct
}

fourElements=( a1 a2 "a3 with spaces" a4 )
twoElements=( b1 b2 )

declare -A assocArray
assocArray[test]="works"

testPassingParams "first" "${fourElements[@]}" "${twoElements[@]}" "single with spaces" assocArray "and more... " "even more..."

test "${assocArray[inside]}" = "adding a new value"

The system will automatically assign:

  • $1 to $hello
  • $anArrayWithFourElements will be an array of params with values from $2 till $5
  • $anotherArrayWithTwo will be an array of params with values from $6 till $7
  • $8 to $anotherSingle
  • $table will be a reference to the variable whose name was passed in as the 9th parameter
  • $anArrayOfVariedSize will be a bash array containing all the following params (from $10 on)

In other words, not only you can call your parameters by their names (which makes up for a more readable core), you can actually pass arrays easily (and references to variables - this feature needs bash >=4.3 though)! Plus, the mapped variables are all in the local scope. This module is pretty light and works in bash 3 and bash 4 (except for references - bash >=4.3) and if you only want to use it separately from this project, get the file /lib/system/02_named_parameters.sh.

Note: For lengths between 2-10 there are aliases for arrays, such as [string[4]], if you need anything more, you need to use the syntax l=LENGTH [string[]], like shown in the above example. Or, make your own aliases :).

Using import

After bootstrapping, you may use import to load either the library files or your own files. The command will ensure they're only loaded once. You may either use a relative path from the file you're importing, a path relative to the file that first included the framework, or an absolute path. .sh suffix is optional. You can also load all the files inside of a directory by simply including the path to that directory instead of the file.

Using try & catch

import util/tryCatch
import util/exception # needed only for Exception::PrintException

Sample usage:

try {
    # something...
    cp ~/test ~/test2
    # something more...
} catch {
    echo "The hard disk is not connected properly!"
    echo "Caught Exception:$(UI.Color.Red) $__BACKTRACE_COMMAND__ $(UI.Color.Default)"
    echo "File: $__BACKTRACE_SOURCE__, Line: $__BACKTRACE_LINE__"

    ## printing a caught exception couldn't be simpler, as it's stored in "${__EXCEPTION__[@]}"
    Exception::PrintException "${__EXCEPTION__[@]}"
}

If any command fails (i.e. returns anything else than 0) in the try block, the system will automatically start executing the catch block. Braces are optional for the try block, but required for catch if it's multiline.

Note: try is executed in a subshell, therefore you cannot assign any variables inside of it.

Using Basic Logging, Colors and Powerline Emoji

import util/log
# using colors:
echo "$(UI.Color.Blue)I'm blue...$(UI.Color.Default)"

# enable basic logging for this file by declaring a namespace
namespace myApp
# make the Log method direct everything in the namespace 'myApp' to the log handler called DEBUG
Log::AddOutput myApp DEBUG

# now we can write with the DEBUG output set
Log "Play me some Jazz, will ya? $(UI.Powerline.Saxophone)"

# redirect error messages to STDERR
Log::AddOutput error STDERR
subject=error Log "Something bad happened."

# reset outputs
Log::ResetAllOutputsAndFilters

# You may also hardcode the use for the StdErr output directly:
Console::WriteStdErr "This will be printed to STDERR, no matter what."

Both the colors and the Powerline characters fallback gracefully on systems that don't support them. To see Powerline icons, you'll need to use a powerline-patched font.

For the list of available colors and emoji's take a look into lib/UI/Color.sh. Fork and contribute more!

See Advanced Logging below to learn more about advanced logging capabilities.

Passing arrays, maps and objects as parameters

import util/variable

The Variable utility offers lossless dumping of arrays and associative array (referred here to as maps) declarations by the use of the @get command.

Combined with the util/namedParameters module, you can pass in either as individual parameters.

A more readable way of specifying the will to pass a variable by it's declaration is to simply refer to the variable as $var:yourVariableName.

In bash >=4.3, which supports references, you may pass by reference. This way any changes done to the variable within the function will affect the variable itself. To pass a variable by reference, use the syntax: $ref:yourVariableName.

array someArray=( 'one' 'two' )
# the above is an equivalent of: declare -a someArray=( 'one' 'two' )
# except this one creates a $var:someArray method handler

passingArraysInput() {
  [array] passedInArray

  # chained usage, see below for more details:
  $var:passedInArray : \
    { map 'echo "${index} - $(var: item)"' } \
    { forEach 'var: item toUpper' }

  $var:passedInArray push 'will work only for references'
}

echo 'passing by $var:'

## 2 ways of passing a copy of an array (passing by it's definition)
passingArraysInput "$(@get someArray)"
passingArraysInput $var:someArray

## no changes yet
$var:someArray toJSON

echo
echo 'passing by $ref:'

## in bash >=4.3, which supports references, you may pass by reference
## this way any changes done to the variable within the function will affect the variable itself
passingArraysInput $ref:someArray

## should show changes
$var:someArray toJSON

Standard Library

import util/type

The framework offers a standard library for the primitive types, such as string or array manipulations to make common tasks simpler and more readable.

There are three ways to make use of the standard library.

1. Create variables by their handle-creating declaration

If you create your variables using the oo-framework's handle-creating declarations, you can execute methods of the standard library by referring to your variable as: $var:yourVariable someMethod someParameter.

Available handle-creating declarations:

  • string
  • integer
  • array
  • map
  • boolean

Since bash doesn't support boolean variables natively, the boolean variable is a special case that always needs to be declared and modified using the handle-creating declaration.

Example:

# create a string someString
string someString="My 123 Joe is 99 Mark"

# saves all matches and their match groups for the said regex:
array matchGroups=$($var:someString getMatchGroups '([0-9]+) [a-zA-Z]+')

# lists all matches in group 1:
$var:matchGroups every 2 1

## group 0, match 1
$var:someString match '([0-9]+) [a-zA-Z]+' 0 1

# calls the getter - here it prints the value
$var:someString

2. Invoke the methods with var:

If you didn't create your variables with their handles, you can also use the method var: to access them.

Example:

# create a string someString
declare someString="My 123 Joe is 99 Mark"

# saves all matches and their match groups for the said regex:
declare -a matchGroups=$(var: someString getMatchGroups '([0-9]+) [a-zA-Z]+')

# lists all matches in group 1:
var: matchGroups every 2 1

## group 0, match 1
var: someString match '([0-9]+) [a-zA-Z]+' 0 1

# calls the getter - here it prints the value
var: someString

3. Pipe the variable declaration directly to the method

Finally, you can also pipe the variable declarations to the methods you wish to invoke.

Example:

# create a string someString
declare someString="My 123 Joe is 99 Mark"

# saves all matches and their match groups for the said regex:
declare -a matchGroups=$(@get someString | string.getMatchGroups '([0-9]+) [a-zA-Z]+')

# lists all matches in group 1:
@get matchGroups | array.every 2 1

## group 0, match 1
@get someString | string.match '([0-9]+) [a-zA-Z]+' 0 1

# prints the value
echo "$someString"

Adding to the Standard Library

You can add your own, custom methods to the Standard Library by declaring them like:

string.makeCool() {
  @resolve:this ## this is required is you want to make use of the pipe passing
  local outValue="cool value: $this"
  @return outValue
}

string someString="nice"
$var:someString makeCool
# prints "cool value: nice"

See more info on writing classes below.

Functional/operational chains with the Standard Library and custom classes

import util/type

The type system in Bash Infinity allows you to chain methods together in a similar fashion one might pipe the output from one command to the other, or chain methods in C#, Java or JavaScript (think JQuery's pseudo-monad style).

declare -a someArray=( 'one' 'two' )

var: someArray : \
  { map 'echo "${index} - $(var: item)"' } \
  { forEach 'var: item toUpper' }

# above command will result in a definition of an array:
# ( '0 - ONE' '1 - TWO' )

Methods available in the next chain depend on the return type of the previously executed method.

Writing your own classes

It's really simple and straight-forward, like with most modern languages.

Keywords for definition:

  • class:YourName() - defining a class

Keywords to use inside of the class definition:

  • method ClassName.FunctionName() - Use for defining methods that have access to $this
  • public SomeType yourProperty - define public properties (works in all types of classes)
  • private SomeType _yourProperty - as above, but accessible only for internal methods
  • $this - This variable is available inside the methods, used to refer to the current type
  • this - Alias of $var:this, used to invoke methods or get properties of an object
  • NOT YET IMPLEMENTED: extends SomeClass - inherit from a base class

After a class has been defined, you need to invoke Type::Initialize NameOfYourType or Type::InitializeStatic NameOfYourStaticType if you want to make your class a singleton.

Here's an example that shows how to define your own classes:

import util/namedParameters util/class

class:Human() {
  public string name
  public integer height
  public array eaten

  Human.__getter__() {
    echo "I'm a human called $(this name), $(this height) cm tall."
  }

  Human.Example() {
    [array]     someArray
    [integer]   someNumber
    [...rest]   arrayOfOtherParams

    echo "Testing $(var: someArray toString) and $someNumber"
    echo "Stuff: ${arrayOfOtherParams[*]}"

    # returning the first passed in array
    @return someArray
  }

  Human.Eat() {
    [string] food

    this eaten push "$food"

    # will return a string with the value:
    @return:value "$(this name) just ate $food, which is the same as $1"
  }

  Human.WhatDidHeEat() {
    this eaten toString
  }

  # this is a static method, hence the :: in definition
  Human::PlaySomeJazz() {
    echo "$(UI.Powerline.Saxophone)"
  }
}

# required to initialize the class
Type::Initialize Human

class:SingletonExample() {
  private integer YoMamaNumber = 150

  SingletonExample.PrintYoMama() {
    echo "Number is: $(this YoMamaNumber)!"
  }
}

# required to initialize the static class
Type::InitializeStatic SingletonExample

Now you can use both the Human and the SingletonExample classes:

# create an object called 'Mark' of type Human
Human Mark

# call the string.= (setter) method
$var:Mark name = 'Mark'

# call the integer.= (setter) method
$var:Mark height = 180

# adds 'corn' to the Mark.eaten array and echoes the output
$var:Mark Eat 'corn'

# adds 'blueberries' to the Mark.eaten array and echoes the uppercased output
$var:Mark : { Eat 'blueberries' } { toUpper }

# invoke the getter
$var:Mark

# invoke the method on the static instance of SingletonExample
SingletonExample PrintYoMama

Writing Unit Tests

import util/test

unit tests

Similarly to Bats, you can use the unit test module to test Bash scripts or any UNIX program. Test cases consist of standard shell commands. Like Bats, Infinity Framework uses Bash's errexit (set -e) option when running test cases. Each test is run in a subshell, and is independent from one another. To quote from Bats:

If every command in the test case exits with a 0 status code (success), the test passes. In this way, each line is an assertion of truth.

If you need to do more advanced testing, or need to be able to run your tests on shells other than bash 4, I'd still recommend Bats.

Example usage:

it 'should make a number and change its value'
try
    integer aNumber=10
    aNumber = 12
    test (($aNumber == 12))
expectPass

it "should make basic operations on two arrays"
try
    array Letters
    array Letters2

    $var:Letters push "Hello Bobby"
    $var:Letters push "Hello Maria"

    $var:Letters contains "Hello Bobby"
    $var:Letters contains "Hello Maria"

    $var:Letters2 push "Hello Midori,
                        Best regards!"

    $var:Letters2 concatAdd $var:Letters

    $var:Letters2 contains "Hello Bobby"
expectPass

Can you believe this is bash?! ;-)

Advanced Logging

import util/log

Here's an example of how to use the power of advanced logging provided by the Infinity Framework.

In every file you are logging from, you may name the logging scope (namespace). If you won't do it, it'll be the filename, minus the extension. It's better to name though, as filenames can conflict. Thanks to scopes, you can specify exactly what and how you want to log.

namespace myApp

## ADD OUTPUT OF "myApp" TO DELEGATE STDERR
Log::AddOutput myApp STDERR

## LET'S TRY LOGGING SOMETHING:
Log "logging to stderr"

The above will simply print "logging to stderr" to STDERR. As you saw we used the logger output called "STDERR". It is possible to create and register your own loggers:

## LET'S MAKE A CUSTOM LOGGER:
myLoggingDelegate() {
    echo "Hurray: $*"
}

## WE NEED TO REGISTER IT:
Log::RegisterLogger MYLOGGER myLoggingDelegate

Now, we can set it up so that it direct only logs from a specific function to the our custom logger output:

## WE WANT TO DIRECT ALL LOGGING WITHIN FUNCTION myFunction OF myApp TO MYLOGGER
Log::AddOutput myApp/myFunction MYLOGGER

## LET'S DECLARE THAT FUNCTION:
myFunction() {
    echo "Hey, I am a function!"
    Log "logging from myFunction"
}

## AND RUN:
myFunction

The above code should print:

Hey, I am a function!
Hurray: logging from myFunction

As you can see, logging automatically redirected the logger from our function from our previously registered STDERR to our more specifically defined MYLOGGER. If you wish to keep logging to both loggers, you can disable the specificity filter:

Log::DisableFilter myApp

Now if we run the function myFunction:

The output will be:

Hey, I am a function!
Hurray: logging from myFunction
logging from myFunction

We can be even more specific and redirect messages with specific subjects to other loggers, or mute them altogether:

## Assuming we're in the same file, let's reset first
Log::ResetAllOutputsAndFilters

Log::AddOutput myApp/myFunction MYLOGGER

myFunction() {
    echo "Hey, I am a function!"
    Log "logging from myFunction"
    subject="unimportant" Log "message from myFunction"
}

And let's change our custom logger a little, to support the subject:

myLoggingDelegate() {
    echo "Hurray: $subject $*"
}

Now when we run myFunction, we should get:

Hey, I am a function!
Hurray:  logging from myFunction
Hurray: unimportant message from myFunction

To filter (or redirect) messages with subject unimportant within myFunction of myApp's file:

Log::AddOutput myApp/myFunction/unimportant VOID

To filter any messages with subject unimportant within myApp's file:

Log::AddOutput myApp/unimportant VOID

Or any messages with subject unimportant anywhere:

Log::AddOutput unimportant VOID

Now, running myFunction will print:

Hey, I am a function!
Hurray: logging from myFunction

How to use?

  1. Clone or download this repository. You'll only need the /lib/ directory.

  2. Make a new script just outside of that directory and at the top place this:

    #!/usr/bin/env bash
    source "$( cd "${BASH_SOURCE[0]%/*}" && pwd )/lib/oo-bootstrap.sh"
  3. You may of course change the name of the /lib/ directory to your liking, just change it in the script too.

  4. Out-of-box you only get the import functionality. If you wish to use more features, such as the typing system, you'll need to import those modules as follows:

    # load the type system
    import util/log util/exception util/tryCatch util/namedParameters
    
    # load the standard library for basic types and type the system
    import util/class
  5. To import the unit test library you'll need to import lib/types/util/test. The first error inside of the test will make the whole test fail.

  6. When using util/exception or util/tryCatch don't use set -o errexit or set -e - it's not necessary, because error handling will be done by the framework itself.

Contributing

Feel free to fork, suggest changes or new modules and file a pull request. Because of limitations and unnecessary complexity of the current implementation we're currently brainstorming a 3.0 rewrite in #45.

The things that I'd love to add are:

  • unit tests for all important methods
  • port to bash 3 (preferably a dynamic port that imports the right file for the right version)
  • a web generator for a single file version of the boilerplate (with an option to select modules of your choice)
  • more functions for the standard library for primitive types (arrays, maps, strings, integers)
  • useful standard classes are very welcome too

Porting to Bash 3

The main challenge in porting to bash 3 lays with creating a polyfill for associative arrays (probably by using every other index for the keys in an array), which are used by the type system. The other challenge would be to remove the global declarations (declare -g).

Acknowledgments

If a function's been adapted or copied from the web or any other libraries out there, I always mention it in a comment within the code.

Additionally, in the making of the v1 of Bash Infinity I took some inspiration from object-oriented bash libraries:

More bash goodness:

bash-oo-framework's People

Contributors

adanielvv avatar bborysenko avatar benstiglitz avatar cj-bc avatar gitter-badger avatar masavini avatar matthewbregg avatar mmcc007 avatar nicobrinkkemper avatar niieani avatar nkakouros avatar philipp-classen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bash-oo-framework's Issues

Exit code is always 0

Is there are way to use custom exit code?

Exit code is always 0

#!/usr/bin/env bash

source "$( cd "${BASH_SOURCE[0]%/*}" && pwd )/lib/oo-bootstrap.sh"
import util/log util/exception util/tryCatch util/namedParameters util/class

exit 1
$ ./your-script.sh; echo $?
0

Overide Exception::CleanUp()

#!/usr/bin/env bash

source "$( cd "${BASH_SOURCE[0]%/*}" && pwd )/lib/oo-bootstrap.sh"
import util/log util/exception util/tryCatch util/namedParameters util/class

Exception::CleanUp() {
  local exitVal=$?
  rm -f $__oo__storedExceptionLineFile $__oo__storedExceptionSourceFile $__oo__storedExceptionBacktraceFile $__oo__storedExceptionFile || exit 100
  exit $exitVal
}

exit 1
$ ./your-script.sh; echo $?
1

Exit command inside class

#!/usr/bin/env bash

source "$( cd "${BASH_SOURCE[0]%/*}" && pwd )/lib/oo-bootstrap.sh"
import util/log util/exception util/tryCatch util/namedParameters util/class

Exception::CleanUp() {
  local exitVal=$?
  rm -f $__oo__storedExceptionLineFile $__oo__storedExceptionSourceFile $__oo__storedExceptionBacktraceFile $__oo__storedExceptionFile || exit 10
  exit $exitVal
}

class:Webhook() {

  Webhook.gitlab() {
    echo "GITLAB"
  }

  Webhook.handle() {
    [string] id

    case "$id" in
      gitlab)
        this $id
        ;;
      *)
        echo "Error"
        exit 1
        ;;
    esac
  }
}

Type::Initialize Webhook

Webhook webhook
$var:webhook handle $1
$ ./your-script.sh gitlab; echo $?
GITLAB
0
$ ./your-script.sh slack; echo $?
Error
0

Aliases not supported in older versions of BASH.

Aliases are simply not supported by the legacy Bourne shell which predates functions. There is no point trying to define and execute an alias under /sbin/sh or /bin/sh when running Solaris 10 and older. The Solution again is to determine the current OS and decide whether to use the Alias or Function.

Possible incomplete solution, declare if older bash detected.
if [[ "${BASH_VERSION:0:1}" -lt 4 ]] ; then
P="x"
declare -${P} import="System::Import"
declare -${P} source="__oo__allowFileReloading=true System::ImportOne"
declare -${P} .="__oo__allowFileReloading=true System::ImportOne"
else
P="g"
alias import="System::Import"
alias source="__oo__allowFileReloading=true System::ImportOne"
alias .="__oo__allowFileReloading=true System::ImportOne"
fi

Replace import with ${import:-import}
Should use import if value not null or literal "import" if null and alias is available.

Side issue:
declare -${P} .="__oo__allowFileReloading=true System::ImportOne" causes error: .../lib/oo-bootstrap.sh: line 11: declare: `.=__oo__allowFileReloading=true System::ImportOne': not a valid identifier

Translating README into Japanese

[This is just a mention for people who is trying to translate/ translating into Japanese]
[日本語訳作業をしている人/しようと思っている人へのメモです]

This is not a issue about bug-report, feature, and something like that.
バグレポートや新しい機能に関するissueではありません。

--- Japanese
oo-frameworkのREADMEを日本語訳しています。
他の人がやってなければ日本語訳やろうかな...?と思っている方いましたら一緒にやりましょう!
大体の部分は翻訳が終わり、細かい訳と手直し作業が残っています。
自分はまだ英語勉強中な身なのでおそらくより良い訳があると思うのです...
協力してくださる方、待っています!(実は少し詰まってたりするので)

--- English
Hi, I'm translating README into Japanese these days.
If you're thinking of translation for Japanese, why not doing together!
It's almost close to end, but I have to check the details and fix mistakes.
As I'm just a student learning English at school, I think there should be better translations.
I'm glad if someone join me and could do great job together! ( actually, I have some problem so that my work is stopped now)

Debugging lib/system/06_system.sh

Hi niieani,

I forked your bash-oo-framework to extend the System.Import function for loading modules directly from github repos. Thus I'd like to print out the subject=level{2,3,4} .... lines Unfortunately I don' really understand how to set-up the logger.

I tried this:

#!/usr/bin/env bash

## BOOTSTRAP ##
source "$( cd "${BASH_SOURCE[0]%/*}" && pwd )/lib/oo-framework.sh"

level_debug(){
  echo "level: $*"
}

Log.RegisterLogger level4 level_debug
Log.RegisterLogger level3 level_debug
Log.RegisterLogger level2 level_debug


## MAIN ##

import lib/steffenhoenig/crash/modules/example
import lib/type-core

blanks are printed instead of stack trace in simple example

My first attempt to use this project failed.

#!/bin/bash

source "$( cd "${BASH_SOURCE[0]%/*}" && pwd )/lib/oo-bootstrap.sh"
import util/exception

ta() {
  touch /untouchable
}
ta

running it:

$ /t/bash-oo-framework/x.sh
touch: cannot touch '/untouchable': Permission denied                                                                                                                                         
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
                                                                                                                                                                                              
 ! Press [CTRL+C] to exit or [Return] to continue execution.                                                                                                                                  
        

I'm on bash 4.3.48(1)-release, and for this project, 903fd74

On debian 9 using lxterminal I had to modify lib/util/test.sh to make unit test work.

The test is the sample test on README.md:
#!/bin/bash
source "$( cd "${BASH_SOURCE[0]%/*}" && pwd )/lib/oo-bootstrap.sh"
import util/test UI/Color
it 'should make a number and change its value'
try
integer aNumber=10
aNumber=12
test $aNumber == 12
expectPass

When the test is runned the next error is shown:
UI.Color.Red: order not found
UI.Color.Default: order not found

The problem happens on line 70 of test.sh:
alias caught="echo "CAUGHT: $(UI.Color.Red)$BACKTRACE_COMMAND$(UI.Color.Default) in $BACKTRACE_SOURCE:$BACKTRACE_LINE""

Why it did as a workaround was to put the aliases on the same file just above the conflicting line:
alias UI.Color.Default="echo"
alias UI.Color.Red="echo"
alias describe='Test NewGroup'
alias summary='Test DisplaySummary'
alias caught="echo "CAUGHT: $(UI.Color.Red)$BACKTRACE_COMMAND$(UI.Color.Default) in $BACKTRACE_SOURCE:$BACKTRACE_LINE""

And all work.

The lxterminal I'm using uses 8 colors:.

[question] Logging to File, Syslog or Email

Hi,

I would like to start using this framework instead of log4sh and I want to kindly ask you if there is any example or if somebody can guide me on how to log to a file, syslog or send the logs thru email?

Thank you.

integrate oo with the modern way using bpkg

Hi everyone 😄

oo is fantastic job in the bash world, I'm very impressed with it.

I would like to help the project add to bpkg.io (Lightweight bash package manager )
may be i can update "how to use readme" adding a way to set up it with bpkg ?

#54

no colors on gnome-terminal

ubuntu 14.04 with GNOME Terminal 3.6.2.

possible cause:
tput colors at Colors.sh line 1 returns '8' even if gnome-ternimal is capable of 256 colors.

Catch exception from Classes doesn't work

Hi,

I have this test script with 4 ways to execute mkdir command (for example):

#!/usr/bin/env bash
source "$( cd "${BASH_SOURCE[0]%/*}" && pwd )/lib/oo-bootstrap.sh"
import util/log util/exception util/tryCatch util/namedParameters util/class

# Creating a Test class
class:Test() {
  Test.Mkdir() {
    [string] path
    if [ ! -d "${path}" ]; then
      mkdir "${path}"
    fi
  }

  Test.MkdirTryCatch() {
    [string] path
    if [ ! -d "${path}" ]; then
      try {
        mkdir "${path}"
      } catch {
        echo "There was an error on folder creation!"
        echo "Caught Exception:$(UI.Color.Red) $__BACKTRACE_COMMAND__ $(UI.Color.Default)"
        echo "File: $__BACKTRACE_SOURCE__, Line: $__BACKTRACE_LINE__"
        Exception::PrintException "${__EXCEPTION__[@]}"
      }
    fi
  }
}

Type::Initialize Test
Test Command

# Run mkdir command without class
#mkdir /tmp/a/b

# Run mkdir command without class with try and catch
#try {
#  mkdir "${path}"
#} catch {
#  echo "There was an error on folder creation!"
#  echo "Caught Exception:$(UI.Color.Red) $__BACKTRACE_COMMAND__ $(UI.Color.Default)"
#  echo "File: $__BACKTRACE_SOURCE__, Line: $__BACKTRACE_LINE__"
#  Exception::PrintException "${__EXCEPTION__[@]}"
#}

# Run mkdir command using the Test class
#$var:Command Mkdir "/tmp/a/b"

# Run mkdir command using the Test class with try and catch
#$var:Command MkdirTryCatch "/tmp/a/b"

1. Run mkdir command without class

mkdir /tmp/a/b

Output:

# ./test.sh
mkdir: cannot create directory â/tmp/a/bâ: No such file or directory

 â UNCAUGHT EXCEPTION: __typeCreate_paramNo (1)
   î  â¦ mkdir /tmp/a/b [test.sh:33]

 â¡ Press [CTRL+C] to exit or [Return] to continue execution.

2. Run mkdir command without class with try and catch

try {
  mkdir "${path}"
} catch {
  echo "There was an error on folder creation!"
  echo "Caught Exception:$(UI.Color.Red) $__BACKTRACE_COMMAND__ $(UI.Color.Default)"
  echo "File: $__BACKTRACE_SOURCE__, Line: $__BACKTRACE_LINE__"
  Exception::PrintException "${__EXCEPTION__[@]}"
}

Output:

# ./test.sh
mkdir: cannot create directory ââ: No such file or directory
There was an error on folder creation!
Caught Exception: mkdir "${path}"
File: test.sh, Line: 37
   î  â¦ mkdir "${path}" [test.sh:37]

3. Run mkdir command using the Test class

$var:Command Mkdir "/tmp/a/b"

Output:

# ./test.sh
mkdir: cannot create directory â/tmp/a/bâ: No such file or directory

4. Run mkdir command using the Test class with try and catch

$var:Command MkdirTryCatch "/tmp/a/b"

Output:

# ./test.sh
mkdir: cannot create directory â/tmp/a/bâ: No such file or directory
There was an error on folder creation!
Caught Exception:
File: /usr/local/share/bash-oo-framework/lib/util/command.sh, Line: 58

The problem is that, the exception/error is not cought when I am using the Test Class.
Is there something I need to set/import/etc. ? I am missing something?

Please guide me in the right direction.

Thank you!

Try catch causes docker conatiner crash with multiple sub shells.

I am trying try catch & exception for first time. My Docker container is crashing with basic test script (see below). Tried with Bash 4.3.11 & 4.2. On further analysis I see multiple sub shell's are created and my docker container dies because of resource exhaustion. I might be missing something here though, please let me know if thats the case.

#!/bash-4.2/bash
# BASH INFINITY
#
# Bash infinity OO framework for try/catch/exception handling
# Github - https://github.com/niieani/bash-oo-framework.git
source "$( cd "${BASH_SOURCE[0]%/*}" && pwd )/lib/oo-bootstrap.sh"

## ERROR HANDLING MODULES
#
# try/catch/excpetion
import util/tryCatch
import util/exception


try {
  ls /root2
} catch {
  printf "directory /root2 not found"
} 

[bug?] `throw` is said to be 'undefined command' when `import` fail

Commit hash: 903fd74

While I tried importing my script, this error has been occurred:

 ✘ UNCAUGHT EXCEPTION: Undefined command (throw)
    ➦ Undefined command [oo-bootstrap.sh:84]
    ✘ System::SourcePath "${__oo__path}/${libPath}" "$@" || e="Cannot import $libPath" throw [oo-bootstrap.sh:84]                                                                                         
       ➦ System::ImportOne "$libPath" [oo-bootstrap.sh:96]
          ➦ System::Import [laun.sh:9]
          ✘ import launsh_lib/keys [laun.sh:9]

( launsh.sh and launsh_lib/* is mine)

From my research, I figured out that throw was treated as exit_code in command_not_found_handle:

# piece of result of 'bash -x laun.sh`
+(oo-bootstrap.sh:89): System::ImportOne(): throw
+(exception.sh:30): command_not_found_handle(): local 'IFS=
'
+(exception.sh:33): command_not_found_handle(): [[ throw = \(\ \s\e\t\ \-*\;\ \t\r\u\e* ]]
+(exception.sh:38): command_not_found_handle(): Exception::CustomCommandHandler throw
+(exception.sh:17): Exception::CustomCommandHandler(): return 1
+(exception.sh:38): command_not_found_handle(): true
+(exception.sh:40): command_not_found_handle(): local exit_code=throw

It's strange because exit_code should be $1, not ${FUNCNAME[0]}.
In this time, exit_code should be empty.
Source:

local exit_code="${1}"

Actually, this is strange too:

if [[ "$*" = '( set -'*'; true'* ]] ## TODO: refine with a regex and test

(because $* is expanded to throw)


Unfortunately, I have no idea to solve this, so I post it.

the minimum example code:

#!/usr/bin/env bash                                                                                                                                                                                         

source "$( cd "${BASH_SOURCE[0]%/*}" && pwd )/lib/oo-bootstrap.sh"

import util/log util/exception
import file_not_exist  # try to import non-exist file

echo 'hello!'

imagemagick name conflict

I need imagemagick for taking screenshots in my current bash project.

I tried to add this framework to it, but import does not work for import util/exception:

import-im6.q16: unable to open image `util/exception': No such file or directory @ error/blob.c/OpenBlob/201.

import is a native imagemagick function. Is there a way to work around this, i.e. unbind import before sourcing the framework and afterwards binding import again?

$ which import
/usr/bin/import

[Question] Does value of '__primitive_extension_fingerprint' have special meaning?

I'm making 'float' primitive extension.

The problem is that:
I want to implement it by using integerArray so that we can calculate easily.
But I can't use integerArray as 'fingerprint' uses some alphabets.
I want to change the base __primitive_extension_declaration using only numbers.

If it is a problem, I'll use 'array' instead of 'integerarray'.

throw is not working

When I do:

!/usr/bin/env bash

# Save current path
currentPath=$(pwd)

scriptDirectoryPath="$(cd "${BASH_SOURCE[0]%/*}" && pwd )"

# Load Bash Infinity Framework, import libraries and turn logging on
source "${scriptDirectoryPath}/vendor/bash-oo-framework/lib/oo-bootstrap.sh"
import util/exception util/tryCatch util/log util/variable util/type util/namedParameters UI/Color

e="test" throw

I will get following:

 ✘ UNCAUGHT EXCEPTION: shift (1)
    ➦ shift [exception.sh:41]
       ➦ command_not_found_handle [test.sh:7]
       ✘ e="test" throw [test.sh:7]

 ⚡ Press [CTRL+C] to exit or [Return] to continue execution.

When I comment out line 41 in lib/util/exception.sh of this framework I get

 ✘ UNCAUGHT EXCEPTION: test ()
    e="➦ test" throw [test.sh:12]

 ⚡ Press [CTRL+C] to exit or [Return] to continue execution.

[question] few questions while reading README.md

hello, I'm really interested in this project, and respect it.
I'm trying translating the README.md into Japanese.
While this, I got some questions( might be a fool one) so can I ask some?
(I'm not a native English speaker, I'm still learning English at school. So, I apologize that there might be some rude words/sentences)

1. the meaning of lossless dumping

In Passing arrays, maps and objects as parameters section:

The Variable utility offers lossless dumping of arrays and associative array (referred here to as `maps`) declarations by the use of the `@get` command.

I couldn't get the meaning of lossless dumping.
I googled a lot, but there's no info...
Is it almost the same meaning of 'dumps perfect copy of arrays' ?

2. what's the meaning of 'groups'?

In the README.md, Standard Library section, there's such comments

# lists all matches in group 1:
$var:matchGroups every 2 1

but based on my codereading, I couldn't understand what the 'group 1' is.
what I got is "every method choose every $everyth value starting from $startingIndex. and return it as an array"

## group 0, match 1
$var:someString match '([0-9]+) [a-zA-Z]+' 0 1

this also confuse me...

Functional/operational chains

is it a word of functional languages?


I'm not sure those are because I don't know other meaning of 'group', or I don't know C#/Java/javaScript languages...

I'm glad if you answer me, thanks.

Named parameter named 'command' does not get evaluated

Here is an example script:

#!/bin/bash
source "lib/oo-bootstrap.sh"
import "../Ethical Hacking/EN2720/scripts/vendor/bash-oo-framework/lib/util/namedParameters.sh"

function q {
   @required [string] command
   echo "${command-UNDEFINED}"
}
q 'some value'

Running this prints 'UNDEFINED'.

Example class human not working

I wrote a file main_menu.sh which used: import lib/* human.sh
and then I added the example usage of human as in the README but it didn't work:

✘ UNCAUGHT EXCEPTION: Method Human.name is not defined.
 ➦ Method Human.name is not defined. [type.sh:161]
 ✘ e="Method $methodName is not defined." throw [type.sh:161]
 ➦ Type::InjectThisResolutionIfNeeded "$type.$method" [type.sh:417]
 local resultString=$(__return_self_and_result=true ➦ Type::ExecuteMethod "$type" "$variableName" "$method" "${params[@]}" || { local falseBool="${__primitive_extension_fingerprint__boolean}:false"; __return_self_and_result=true @return falseBool $variableName; }) [type.sh:435]
 ➦ Type::RunCurrentStack [type.sh:676]
 eval "${__oo__variableMethodPrefix}${variableName}() { ➦ Type::Handle $variableName "$@"; }" [type.sh:119]
 ➦ A04FB7D7594E479B8CD8D90C5014E37A:Mark [main_menu.sh:51]
 ✘ $var:Mark name = 'Mark' [main_menu.sh:51]

Is this a bug or am I misunderstanding how to use this?

GNU bash, version 4.4.12(1)-release (x86_64-unknown-linux-gnu)

False boolean not working

The following error occurred

boolean trueVar=true
$var:trueVar toString #true

boolean falseVar=false
$var:falseVar toString #UNCAUGHT EXCEPTION: toString

Is it a bug? Or is my implementation incorrect?

Why are UI.Color members aliases?

I am in.the process of incorporating this framework to a project of mine. I used to have a file full of color definitions in the form of variables. I saw that you provide colors as aliases that use echo. Is there a benefit in doing that? My (naive) intuition that variable should be even a little faster.

Exits and closes the shell window when the test scripts executed

I tried executing a couple of test scripts (try-catch.sh and oo.sh). The shell window was immediately closed when executed.

My bash version is as follows.
GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.

Please give me some guide lines on solving this.

Thanks.

Make wiki page

For now, README.md is the only reference of this framework(except source codes themselve.)
I wonder if we gather all knowledges and make a wiki for tutorial, reference, etc.
I've spent so much time to read code in order to understand the framework.
It's really good experience for me and I enjoy reading, but some people doesn't like it.
(and also, I want reference while making my project)

I'm sure it's not a good time to start because #45 (the RFC for 3.0) is on the way.
But I think it'll take long time to release 3.0 so that it's better to have wiki of version 2.0

(I can add Japanese references)

Unbound variables cause a crash when running examples with 'set -u'

tested in bash 4.3+
when setting 'set -u' in example/array.sh, I receive:
bash-oo-framework/lib/util/command.sh: line 34: $3: unbound variable

there are more errors elsewhere, after performing "${unboundvar:-}"

Is this conscious design decision related to bash version?
Sometimes I want my script to fail, not to cause more mayhem in unpredictable way. How to overcome this?

How do you work with variables outsite of the try catch but changing its value inside?

Hi,

I have some function that change some variable inside the try/catch and return 0 or 1.

My first problem is when I return 0 or 1 it throws an error.

My second problem is: I placed the return 0 or 1 outside of the try/catch, but I change some variable inside the try/catch. And then I cannot see the new value of this variable.

Kind Regards,
Felipe

Boolean named function parameters

I have a function that accepts a "boolean" as an argument. I want to make this argument named. I have:

import util/namedParameters
set -u

function q {
  [boolean] debug
  if [ "$debug" = "true" ]; then
    set -x
  else
    set +x
  fi
}
q true

Now, due to set -u the script fails as there is an unbound varible __primitive_extension_fingerprint__boolean. This variable is defined in util/types.

  • If I remove set -u the $debug variable has a value of :false.
  • If I copy and paste __primitive_extension_fingerprint__boolean from utils/types into utils/namedParameters, the script succeeds. But the variable's value has a long alphanumeric prefix.

In both cases, my check `[ "$debug" = "true"] fails. How should I check for the value of the boolean?

Inconsistent array assignments

There is a problem when trying to assign array by value to any variable after initialization. I prepared code sample to demonstrate my issue. To my knowledge every case should work exactly the same. What am I missing here?

#!/usr/bin/env bash

source "$( cd "${BASH_SOURCE[0]%/*}" && pwd )/lib/oo-bootstrap.sh"

import util/log
import util/class

class:MyIssue() {
  public string someMessage
  public array someMessageLines

  MyIssue.someStuff() {
    array someMessageLines="$(this someMessage toArray)"
    $var:someMessageLines
    if [[ $($var:someMessageLines length) != 0 ]]; then echo 'This stuff worked '; fi
  }
  MyIssue.someStufff() {
    array someMessageLines
    $var:someMessageLines="$(this someMessage toArray)"
    $var:someMessageLines
    if [[ $($var:someMessageLines length) != 0 ]]; then echo 'This stufff worked '; fi
  }
  MyIssue.someStuffff() {
    this someMessageLines="$(this someMessage toArray)"
    this someMessageLines
    if [[ $(this someMessageLines length) != 0 ]]; then echo 'This stuffff worked '; fi
  }
} 
Type::Initialize MyIssue

MyIssue my
$var:my someMessage = 'automatic
error
handling
with
exceptions
'

$var:my someStuff
echo '----------------'
$var:my someStufff
echo '----------------'
$var:my someStuffff

Terminal not restored on cygwin

The error handling framework shows the error on Cygwin (some characters look odd, though) and then offers to exit or continue.

When I press Ctrl+C to exit, I get a new prompt but the terminal is in a bad state - I can't see what I type anymore. I have to blindly type "stty sane" to reset the terminal.

I'm not sure why that happens. I tried read -s in the terminal and aborted it with Ctrl+C. The editing was restored in this case, so it must be something else. The escape codes in line 232 of https://github.com/niieani/bash-oo-framework/blob/master/lib/system/03_exception.sh (c282937) also doesn't look like it's leaking something.

Maybe a trap handler?

I'm using a recent version of Cygwin with BASH 4.3.42.

Cannot import lib/types/base/Var.sh

I'm using Bash 4.3.42(1)-release and just checked out the project.

Trying to call ./oo.sh results in:

 ✘ UNCAUGHT EXCEPTION: Manually invoked
## 58
    ➦ Cannot import /src/bash-oo-framework/lib/types/base/Var.sh [06_system.sh:53]
    ✘ [ ! -e "$libPath" ] && throw "Cannot import $libPath" && return 1 [06_system.sh:53]
## 1
       import Var [Array.sh:1]
## 19
          ➦ source "$libPath" || throw "Unable to load $libPath" [06_system.sh:19]
## 66
             ➦ System.LoadFile "$file" [06_system.sh:66]
## 9
                ➦ System.Import [oo.sh:4]
                ✘ import lib/types/base [oo.sh:4]

 ⚡ Press [CTRL+C] to exit or [Return] to continue execution.

Try/Catch syntax error

Hi,

i'm trying to implement try/catch util but no success so far.

Following code

try {
    # many lines of code
} catch {
   # Handling error
}

Gives me an error:
script.sh: syntax error near unexpected token }' script.sh: line 159: } catch {'

Any clue what's wrong with try catch syntax and/or implementation?

How should call stacks be read?

I have the following stack trace:

screenshot_20180117_002703

This is how I interpret the output. Could you correct me if I am wrong?
The "important" lines are the ones starting with the arrow (the underlined, red ones). I call them main lines. The first of these, the top-most, contains a friendly error message. The others show at most 3 things:

  • the red part is the function in whose call the error happened
  • the white part are the arguments passed to that function
  • the blue part which is the file and line of the function call

Then, some times there might be a second accompanying line for these main lines. These accompanying lines are present when the main line does not provide the full picture, ie the actual function/builtin call with the arguments passed.

Is the above correct?

Some more questions:

  1. What does the 'X' icon in the accompanying lines mean?
  2. In the example picture, the second call from the top is to the Variable::TrapAssignNumberedParameter function. But the accompanying line seems out of place. Is this due to the magic with the named parameters aliases and traps?

Complex example

Hi, I just love your framework (finally I don't see myself lost coming from C# and Swift worlds), but is there any complex example? I'm currently looking for these:
1] How do you create collections of class objects?
2] How to have class properties which are based on other classes? I mean:

class:Location() {
  public string x
  public string y
}

class:Vehicle() {
  public Location location
  public string name
}

Is it possible at all?

Thank you.

Unable to load lib/types/ui/color.sh

Hi Bazyli,

after you resolved #7, I'm running into another problem:

 ✘ UNCAUGHT EXCEPTION: Manually invoked
    ➦ Unable to load /src/bash-oo-framework/lib/types/ui/color.sh [06_system.sh:14]
    ✘ source "$libPath" || throw "Unable to load $libPath" [06_system.sh:14]
       ➦ System.LoadFile "$file" [06_system.sh:66]
          ➦ System.Import [oo.sh:5]
          ✘ import lib/types/ui [oo.sh:5]

Thanks.

Referring this in Human example class doesn't work as described

Human.Eat() {
    [string] food

    this eaten push "$food"

    # will return a string with the value:
    @return:value "$this just ate $food, which is the same as $1"
  }

This function prints out just ate corn, which is the same as corn

It should be like this:

Human.Eat() {
    [string] food

    this eaten push "$food"

    # will return a string with the value:
    @return:value "$(this name) just ate $food, which is the same as $1"
  }

This function prints out Mark just ate corn, which is the same as corn

#85

[RFC] 3.0: Scope, features and priorities

Continuing from #44 (comment).

@tterranigma Let's discuss the scope of 3.0.

I've added you as a collaborator so you have push rights to the repo.

We should decide on the features to focus on implementing and problems needing tackling first. I've opened a clean branch (3.0) so we can work with a clean slate.

We can keep the outcome of the discussion as documentation inside of the repo itself, e.g. in a docs folder.

Some things that come to mind while designing the future:

Module system

Any respecting language ecosystem has a module system for managing and sharing dependencies/libraries (JS has npm, Python has PyPi, Ruby has RubyGem, C# has NuGet, and so on). I think we should think about both a method of sharing internal and external "modules", which is especially difficult, since functions are global in bash.

For a non-polluting way of loading, I propose something in the lines of my experiment here.

For a shared repository of public (and private) libraries we could use GitHub, just as Go does it.

Another useful feature that I thought about is would be "bundling", i.e. replacing and inlining all imports, in order, with actual code. This would be great for distributing, since you could use all the features you want and just move/upload a single script file that has all of the functionality embedded.

Features from 2.0

Which are the most important to port first?

  • I'm probably not that keen on doing OO, but we could provide a standard library of sorts for simplifying/unifying builtin features (like REGEXP).
  • Error reporting is probably high on the list.
  • New implementation of named parameters.
  • Passing arrays/dictionaries as arguments (should go well with new named params).
  • Things like colors/emojis/powerline, which are TERM specific. How to handle them safely and with high usability (we had bugs there in 2.0)?

Other ideas

  • Compatibility with bashdb for debugging would be really useful.
  • Integration with other popular tools.
  • Good, composable, declarative parsing of CLI arguments/parameters. Perhaps something like DocOpt?
  • Side effect functions: I've had this idea that we could have a format for the result of a function that could provide side-effects in the upper scope. This would enable things like modifying the current scope from within subshells.

Thoughts?

Not running on bash <= 4.1

Have this error when running your-script.sh on Solaris.

$ uname -a
SunOS localhost 5.11 11.2 sun4v sparc sun4v
$ bash --version
GNU bash, version 4.1.17(1)-release (sparc-sun-solaris2.11)
$ ./your-script.sh
/pkg/my/tools/bash-oo/lib/oo-bootstrap.sh: line 44: declare: -g: invalid option
declare: usage: declare [-aAfFilrtux] [-p] [name[=value] ...]
/pkg/my/tools/bash-oo/lib/oo-bootstrap.sh: line 45: declare: -g: invalid option
declare: usage: declare [-aAfFilrtux] [-p] [name[=value] ...]
/pkg/my/tools/bash-oo/lib/oo-bootstrap.sh: line 176: declare: -g: invalid option
declare: usage: declare [-aAfFilrtux] [-p] [name[=value] ...]
/pkg/my/tools/bash-oo/lib/oo-bootstrap.sh: line 177: declare: -g: invalid option
declare: usage: declare [-aAfFilrtux] [-p] [name[=value] ...]
/pkg/my/tools/bash-oo/lib/oo-bootstrap.sh: line 178: declare: -g: invalid option
declare: usage: declare [-aAfFilrtux] [-p] [name[=value] ...]
/pkg/my/tools/bash-oo/lib/oo-bootstrap.sh: line 190: declare: -g: invalid option
declare: usage: declare [-aAfFilrtux] [-p] [name[=value] ...]

Not running on bash version <= 3

I'm sure I could hack away at this but it seems that there's a hold up here that's preventing this from running just out of the box (directly cloned, run oo.sh) as a simple smoke test.

 [themarmot@bq bash-oo-framework] :) ./oo.sh
 /Users/themarmot/Projects/buyerquest/itops/nextgen/terraform-dev/bash-oo-framework/lib/oo-framework.sh: line 47: declare: -g: invalid option
 declare: usage: declare [-afFirtx] [-p] [name[=value] ...]
 /Users/themarmot/Projects/buyerquest/itops/nextgen/terraform-dev/bash-oo-framework/lib/oo-framework.sh: line 48: declare: -g: invalid option
 declare: usage: declare [-afFirtx] [-p] [name[=value] ...]
 /Users/themarmot/Projects/buyerquest/itops/nextgen/terraform-dev/bash-oo-framework/lib/oo-     framework.sh: line 49: declare: -g: invalid option
 declare: usage: declare [-afFirtx] [-p] [name[=value] ...]
 /Users/themarmot/Projects/buyerquest/itops/nextgen/terraform-dev/bash-oo-framework/lib/oo-framework.sh: line 16: /system/*.sh: No such file or directory

Named parameters break if the function returns immediately after parameter definition

Consider this example bash file:

function q() {
  [string] var
}

In the above example, there is no command for the DEBUG trap to hook into. This is expected.

Now consider this:

function q(){
  @required [array] var
  for i in "${var[@]}"; do
    echo $i
  done
}

q "$(@get array_var)"
echo 'Done'

In the above, (it seems that) the var array will be set when the for i in "${var[@]}"; do line gets run. But bash will not even run this line as there is no array to expand. So, it will simply skip the loop and return from the function as there is no other command to run. As a result, the context will change and __assign_paramNo and the other variables will be unset. The @required is there just for the example, it is not crucial to repeating the behavior. If there were more lines after the loop in the function, bash would still skip the loop, but the array would eventually be evaluated when the line after the loop was run.

I haven't found a way to solve this.

Hardcoded tempfile names -> security vulnerabilities

Use of hardcoded names referring to world-writable locations, ie. /tmp/stored_exception, mean that an attacker can create symlinks to a file they don't themselves have write privileges to in such a location, and rely on separate program using the infinity framework to overwrite that symlink's target.

(See also concurrency issues).

required packages

the framework works out of the box in my current ubuntu.

I have a docker container using debian9 and bash 4.4.12. I am getting output like this

igor@04e68d65320c:~/containerroot$ ./igorpro --registerfile serial.txt 
activating bash-oo-framework









 ! Press [CTRL+C] to exit or [Return] to continue execution.
{*}  Continuing...


 ! Press [CTRL+C] to exit or [Return] to continue execution.

where the error description and backtrace is missing. Do I need to install any required packages or fonts - I could not find anything.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.