Dec 22, 2012

Object Oriented Programming in Bash

Bash is a very common *nix shell, and it's programming language is purely procedural and focused on command execution. Object Oriented Programming (OOP) is a programming paradigm that represents the elements of a problem as entities with a set of properties and actions that it can execute. If you use Bash to write very simple and short scripts, procedural programming is just fine, you don't need more. But if your program becomes more and more bigger, a monster program (> 1000 lines), then you need a better way to structure your program to make it easy to maintain and read. Of course, Bash lacks OOP features, but with some tricks you can simulate it with a few lines added, and I'll show you how.

ATTENTION: You need to have solid concepts of OOP and Bash before reading it, I will not explain nothing of that here.

First at all create an script in which you will define your class, and give it execution permissions.

Explanation

This is function is an equivalent of the constructor of a class. It must have the same name of the class. The name of a class can't contains underscores (_). We will use this function to create instances of the "Vector" class.

Here you can get a reference of the base class of the object and a reference of the object created. The first argument of the constructor must be always the name of the object to create.

This block of code simulates multiple inheritance, is optional and you can remove it if you don't need.

Every property and method of the class can be accessed as ${this}_methodProperty_name, or you can also call to it's base class as ${base}_methodProperty_name.
Multiple inheritance works overwriting the methods and properties (m&p) of the preceding classes as follows:

Class1 < Class2 < Class3 < Base

Class2 overwrites m&p of Class1.

Class3 overwrites m&p of Class1 and Class2.

Base overwrites m&p of Class1 and Class2 and Class3.

This will export the m&p of the base class as a m&p of the object. See bellow.

Every method and property of the class is exported as a global variable in the form of objectName_methodProperty_name. You can initialize it's properties through the constructor's parameters, or with a default value, or leave it uninitialized.

This will find every function with the pathern BaseClass_* and export it as a global variable replacing BaseClass with the object's name, the value of the variable will be a string with the base class method and the object name as first parameter that serves as a reference to it.

The methods of the class are defined as BaseClass_methodName. Method names can contains underscores, but camelCase is recommended.

Obtains a reference to the base class (not obligatory) and the object, remember, the first argument is the name of the object.

Of course, you can create new objects inside the methods of the same class.

We are ready to use our class. Create an script from which you will use your class, give it execution permissions, and copy the following code inside it:

Hi,Thanks a lot for explaining this topic!I have some trouble trying to understand how these lines work:export ${property/#$class\_/$this\_}="${property}" # Why not '${property} ${this}' as in the line below?export ${method/#$class\_/$this\_}="${method} ${this}"$(eval "echo \$${this}_inherits") # Curious about '\$$' syntaxIt'll be great if someone will kindly explain or point me to some relevant readings. :)

I have some trouble trying to understand how these lines work:export ${property/#$class\_/$this\_}="${property}" # Why not '${property} ${this}' as in the line below?export ${method/#$class\_/$this\_}="${method} ${this}"

Because a method needs to know who is calling for applying it's operations to the right object, while a property already contains a reference to it's owner object.

$(eval "echo \$${this}_inherits") # Curious about '\$$' syntax

The first \$ is just the literal character $ inside the string, the second $ is part of ${this} that will be replaced for the contents of the this variable.

In the example, you must read the expression:

eval "echo \$${this}_inherits"

as follows:

1) Replace ${this} by Vector:

eval "echo \$Vector_inherits"

2) Since $Vector_inherits is a dynamic variable and I need to read it's contents, I eval the expression inside the quotes: eval "...", that is echo $Vector_inherits.

Can you please explain , why are we declaring the variable base as ==> base=$(expr "$FUNCNAME" : '\([a-zA-Z][a-zA-Z0-9]*\)') at line number 42 and 55 , as we are not using this variable inside those functions in vector.sh??

Also could you provide some more examples where we are actually making use of the inherited classes ==> export ${this}_inherits="Class1 Class2 Class3" # (3.1)

I am very interested to learn more on OOPS in bash script. Can you help me in knowing some good book/site/material ??

$base is declared in case you want to access to some method or property from the base class, I didn't used because it was not required for the example.

Anyways, you are right, the example was too simple, I'll try to make it more complete in the future, right now I'm very busy.

About books talking about this topic, mmh..., thats really hard to find. OOP isn't natively supported by Bash, so what presented here was a pure hackish way to do it. As @Daniel Lemke, linked here, there are many ways to mimic OOP in Bash, just search for it ;).

I like the use of the Class_func call inside a variable name passing the object instance name as the first parameter.I found that I often need to loop over a list of objects. In this case, my variable contains the object nameso I need an extra level of de-referencing with an extra eval.

I want to be able to do this: for PtrPbj in AOBJ BOBJ COBJ ; do eval \$${PtrObj}_initialize donewithout the extra eval and \$

eg. If my object is named AOBJ and I have a reference to it like this PtrAOBJ="AOBJ" then when I want to callAOBJ_func using PtrAOBJ, I need to do this:

val \$${PtrAOBJ}_func "param_to_AOBJ_func"

If instead of creating variables like export AOBJ_func="Class_func AOBJ"I create a functionAOBJ_func() { Class_func AOBJ $@; }then I can do one less level of eval by calling it like this:${PtrAOBJ}_func "param_to_func" # works because it expands to AOBJ_func which can be evaluated directly without another eval.

Previously I was trying to re-parse the whole class function for every instance, butnow with this simple 1 line function, I can get the same result.

Thanks a lot! :$I try to publish articles that are on my interest, and that's normally hard to find in other websites/blogs, or not very well explained. I'm not a regular writer, but i try every article to count.

Programming is combination of intelligent and creative work. Programmers can do anything with code. The entire Programming tutorials that you mention here on this blog are awesome. Beginners Heap also provides latest tutorials of Programming from beginning to advance level. Be with us to learn programming in new and creative way.

it will export all found functions (by compgen) and adds the current vector to it, you see it when you run env after you've run this script. i reckon you need a check if the $method contaisn the $base first, before exporting ite.g. if [[ $method == $base_* ]]; then export.....

Nice... staying basic bash-y, but thinking and structuring oo for state and behavior. The challenge though is still the creation of gazillions of globals... as long as bash does not get an extension for structured variables, there is nothing that can prevent that... Writing a 'cap' is a great stop gap solution... did it myself several times, for PL/I and even COBOL, including VM with garbage collection (back in end 80' / early 90').