The TERRIFYING "Object Oriented Programming" power of bash

This text is more or less an intellectual exercise
or maybe even a nice source for discussion

Anyway I just wanted to be the heretic in the OOP room
Yes, that kind of person :wink:

Don't take this too seriously though
it's more about the way of thinking than the actual code.

Working examplecode is included.

INTRODUCTION

Several people have tried to implement OOP in bash by trying to imitate the syntax and constructs of other so called OOP languages, which always ends up as very complex, restricted, cumbersome, labour intensive and highly impractical.

Let's just don't do that, forget about other so called OOP languages
and go back to the roots of what OOP actually is:

Most people agree that a language is fully OOP if it conforms to (most of) the following rules.
In order of importance and if it is supported in bash:

  • encapsulation -> yes
  • messagepassing - >yes
  • dynamic/late binding -> yes
  • data abstraction -> yes
  • objects -> yes
  • classes/prototypes -> yes
  • inheritance -> yes
  • polymorphism -> no ( or maybe, not investigated yet)

The fundamentals of OOP in bash

As known, bash is immensely powerful but not a very subtile tool, so we have to accept this:

  1. every object in bash is a backgound process (encapsulation)
  2. messagepassing is by named pipes (or POSIX-ipc if implemented)

( I suppose Alan Kay would already acknowledge the validity of calling these 'objects' :wink: )

Let's get started

We can create a class/prototype file :

example1.class

name='John'
address='myway_or_the_highway 3'

DrawLine() { echo '----------------------' ;}

We have inheritance:

example2.class

. example1.class

name='Peter'

We have the code:

examplecode.sh

#!/bin/bash

. oop

# create instances

instantiate example1.class obj_one
instantiate example2.class obj_two

# send messages to the objects

msg_to obj_one 'DrawLine'
msg_to obj_one 'echo $name ; DrawLine'
msg_to obj_two 'echo $name ; DrawLine'

# The TERRIFYING but oh so powerful truth

msg_to obj_two 'name=Mark'
msg_to obj_two 'echo $name ; DrawLine'

# destroy instances

msg_to obj_one 'exit'
msg_to obj_two 'exit'

wait # until all subprocesses have ended


we have the output:

----------------------
John
----------------------
Peter
----------------------
Mark
----------------------

The TERRIFYING principle of this bash OOP implementation:

all it does is wrapping a classfile like this:

mkfifo instancename

. classfile

while : ; do eval "$(<"${instancename}")" ; done

And subsequently launching it as background process
Every bash command you send through the pipe is executed in this subshell

Yes, terrifying, but also immensely powerful and flexible.

And finally, this is the working oop file that was sourced in the above examplecode.sh
This is what makes the magic happen:

oop

msg_to() { echo "${2}" >"${1}"; wait ;}

instantiate()
{
	local class="${1}" instance="${2}"

	rm "${instance}" 2>/dev/null ; mkfifo "${instance}" || return 1
	wait
	(
		trap "rm ${instance}" INT TERM EXIT

		. "${class}"

		while : ; do eval "$(<"${instance}")" ; done
	) &
	disown "$!"
}
3 Likes

Absolutely love that you went there!

Since shell programming is my "goto", this will help me, by giving me an overall "eco-system" to keep in mind with my attempts to modularize and extract shared code.

My only concern (is it really?) is that I don't see the process "suspend" (a la Real-Time halt/resume), other than the one "wait", which is only waiting to die, if I read the code correctly.

2 Likes

Yes, completely correct, suggestions welcome ! :slight_smile:

Usually I would fall back on kill -SIGSTOP pid and kill -SIGCONT pid for that
It also means that to get that, I might start to ask the object its pid like:

msg_to obj_one 'echo pid=$$ >somefile'
. somefile

or something like that, although it lacks a bit of elegance.

I was wondering if a mechanism using a folder for process trigger handoff, call it

bash_signals

would have files like

  • middleware_connect
  • middleware_${PID}_hold
  • middleware_${PID}_msg
  • etc.

which might perform the task of of the "batton-passing" flags similar to real-time systems via the "inotify" triggers ??? Or is that just insane as an approach.

Yes, that would be no problem.

No, not insane at all

You mean something like this ?

inotifywait -m -e close_write "$bash_signals" |
    while read watched_filename EVENT_NAMES event_filenameline
    do
        something
    done
1 Like

Yes, as long as it implied no repeated hits to the CPU when "idle".

1 Like

The inotifywait on disk files would allow ongoing visibility of status without having to "probe" the process or to be constantly logging messages, when there would be no need to keep those. :slight_smile:

1 Like

Quite an interesting read.
I think that a discussion could be done in a new topic although I don't know anything about YSH so I can't give any input :smile:

1 Like

That is exactly why it is one of my "goto" commands. I too prefer IPC and signalling over polling.
Strangely enough, that path ( a herd of communicating scripts/processes ) led me to the subject of this topic :joy:

1 Like

I understand completely! I haven't done much in pursuing this approach, as yet, but I was looking for a mechanism that would behave like industrial PLCs (Programmable Logic Controllers), which using inotifywait, combined with what you have sketched above, gives me a (still somewhat blurry) vision of how to pursue that goal! :slight_smile:

Thank you for raising the topic and reminding me of the inotifywait, which I have not touched on in over 4 years.

1 Like