I'm trying to use bash scripting to make an script act like a phone book, so i tried to create classes and objects but unfortunately i couldn't find a way to do that ! so i'm asking how to create a class using bash scripting??
Bash is a scripting language, that doesn't support OOP, so you can't. Try Python.
The only other thing you could do is have several arrays, but that's messy. Use the index to link them.
You can try to do something like this
example.sh
#!/bin/bash
# include class header
. obj.h
. system.h
# create class object
obj myobject
# use object method
myobject.sayHello
# use object property
myobject.fileName = "file1"
system.stdout.printString "value is"
system.stdout.printValue myobject.fileName
obj.h
obj(){
. <(sed "s/obj/$1/g" obj.class)
}
obj.class
# Class named "obj" for bash Object
# property
obj_properties=()
# properties IDs
fileName=0
fileSize=1
obj.sayHello(){
echo Hello
}
obj.property(){
if [ "$2" == "=" ]
then
obj_properties[$1]=$3
else
echo ${obj_properties[$1]}
fi
}
obj.fileName(){
if [ "$1" == "=" ]
then
obj.property fileName = $2
else
obj.property fileName
fi
}
system.h
. system.class
system.class
system.stdout.printValue(){
echo $($@)
}
system.stdout.printString(){
echo $@
}
Link for reference: https://github.com/mnorin/bash-scripts/tree/master/objects The point is you can't create objects but you can emulate object-oriented programming in bash.
UPD: After all these years I actually decided to add a hint for "inheritance", because there is obviously no way to implement an actual inheritance, ALTHOUGH, combination may be visually presented like one. And to go this way we already have pretty much everything in the first example. With all the scripts above we can do a very simple thing like this:
obj2.h
obj2(){
. <(sed "s/obj2/$1/g" obj2.class)
}
obj2.class
# Class named "obj2" for bash Object
obj2.sayGoodbye(){
echo Goodbye
}
And the updated version of example.sh
#!/bin/bash
# include class headers
. obj.h
. obj2.h
. system.h
# create class object
obj myobject
obj2 myobject
# use object methods
myobject.sayHello
myobject.sayGoodbye # method "inherited" from obj2
myobject.fileName = "file1"
system.stdout.printString "value is"
system.stdout.printValue myobject.fileName
So, if you get lines obj myobject
and obj2 myobject
to look like inherit myobject from obj obj2
, which is very simple, it will look a bit more like an inheritance (of course, it still won't be one).
UPD2:
Following up on comments regarding making it work on bash3. Should be easy fix. Just replace $1
in obj.property with ${!1}
and try to run it.
declare -Ag obj_properties
, you will create a working associative array rather than relying on numerical indices that must be declared. This is beneficial in that you no longer need to declare a fixed number of indices but can actually dynamically generate elements on the fly. –
Odelet source class.h
instead of . class.h
wouldn't be better? There are any implications of using it? –
Beaty .
or source
, since it is not going to run in POSIX shell.. When using bash script, take "full" advantage of bash syntax without worrying about POSIX shell compatibility, but DO try to avoid stuff like mixing other scripting languages such as, sed
, awk
, grep
in your bash script (which can be avoided using patterns). Similarly, when using POSIX shell script, take full advantage of that. This is what I have been taught and I use in practice where cross-platform portability is a concern. –
Anosmia obj_properties[$1]=$3
), so it is also not compatible with bash3. –
Anosmia So I remember checking this question and answer a few years back .. and was thinking.... WHAT!?!?!
Then last week I took a closer look at @Maxims answer and then it became clear..
I have spent the last week and created a bash class transpiler and class loader for class object, with methods and other goodies.. all cause I wanted to create a terminal animation infrastructure:
So while this is just a start I found this to be a SUPER cool and challenging adventure.. I hope my code would help someone else as well!!
BTW: Was tested only on MAC OS so some tweaking might be needed :)
Bash is a scripting language, that doesn't support OOP, so you can't. Try Python.
The only other thing you could do is have several arrays, but that's messy. Use the index to link them.
Try with BashX: https://github.com/reduardo7/bashx (This is my project, I use it on several other projects)
Example
#!/usr/bin/env bash
# ...
@Actions.action1() { # \\n Action without arguments
set -x
pwd
@log "
Action 1
Multi-Line
"
ls -la
bash
}
@Actions.action2() { # param1 [param2] \\n Action with arguments\\n\\tdescription second line\\nother line
eval "$(@user.options 'new:-n|-N' 'path:-p|--path:true')"
set -x
@log "'n' or 'N' parameter: ${user_options_new}"
@log "'p' or 'path' parameter: ${user_options_path[@]} (${#user_options_path[@]})"
local param1="$1"
local param2="$2"
[[ "$param1" != 'asd' ]] && @throw.invalidParam param1
@log Action 2
@log Param1: $1
@log Param2: $2
}
@app.run "$@"
Usage
./myscript action1
./myscript action2 -n -p /tmp 'my param 1'
While there's no true way to create classes in Bash, you can get a little creative. I've found over the years that my preferred way to do this is to create a function that returns a command that can be executed to change state or read properties of the instance. The instance data can be stored in an array.
For example, if you wanted to make a class for binary search trees, you could have this in BinarySearchTree.sh
:
__BINARYSEARCHTREE_INSTANCE_DATA__=()
__BINARYSEARCHTREE_INSTANCE_DATA_LENGTH__=()
__BINARYSEARCHTREE_INSTANCE_DATA_SIZE__=()
BinarySearchTree()
{
echo "__BinarySearchTree__ $RANDOM "
}
__BinarySearchTree__()
{
local id="$1"
local code="$2"
case "$code" in
'.insert' | '[insert]' | "['insert']" | '["insert"]')
local value="$3"
if [ "${__BINARYSEARCHTREE_INSTANCE_DATA__["$id", 0] + set}" ]; then
local length="${__BINARYSEARCHTREE_INSTANCE_DATA_LENGTH__["$id"]}"
local new_node="$length"
__BINARYSEARCHTREE_INSTANCE_DATA__["$id", "$length"]="$value"
length=$((length + 1))
__BINARYSEARCHTREE_INSTANCE_DATA__["$id", "$length"]=''
length=$((length + 1))
__BINARYSEARCHTREE_INSTANCE_DATA__["$id", "$length"]=''
local current_node=0
local parent
while [ 1 ]; do
parent="$current_node"
if [ "$value" -lt "${__BINARYSEARCHTREE_INSTANCE_DATA__["$id", "$((current_node))"]}" ]; then
current_node="${__BINARYSEARCHTREE_INSTANCE_DATA__["$id", "$((current_node + 1))"]}"
if [ "$current_node" == '' ]; then
__BINARYSEARCHTREE_INSTANCE_DATA__["$id", "$((parent + 1))"]="$new_node"
break
fi
else
current_node="${__BINARYSEARCHTREE_INSTANCE_DATA__["$id", "$((current_node + 2))"]}"
if [ "$current_node" == '' ]; then
__BINARYSEARCHTREE_INSTANCE_DATA__["$id", "$((parent + 2))"]="$new_node"
break
fi
fi
done
__BINARYSEARCHTREE_INSTANCE_DATA_LENGTH__["$id"]="$((length + 1))"
__BINARYSEARCHTREE_INSTANCE_DATA_SIZE__["$id"]="$((${__BINARYSEARCHTREE_INSTANCE_DATA_SIZE__["$id"]} + 1))"
else
__BINARYSEARCHTREE_INSTANCE_DATA__["$id", 0]="$value"
__BINARYSEARCHTREE_INSTANCE_DATA__["$id", 1]=''
__BINARYSEARCHTREE_INSTANCE_DATA__["$id", 2]=''
__BINARYSEARCHTREE_INSTANCE_DATA_LENGTH__["$id"]=3
__BINARYSEARCHTREE_INSTANCE_DATA_SIZE__["$id"]=1
fi;;
'.has' | '[has]' | "['has']" | '["has"]')
local value="$3"
local current_node=0
if [ "${__BINARYSEARCHTREE_INSTANCE_DATA__["$id", 0] + set}" ]; then
while [ 1 ]; do
local current_value="${__BINARYSEARCHTREE_INSTANCE_DATA__["$id", "$((current_node))"]}"
if [ "$current_value" == "$value" ]; then
return 0
fi
if [ "$value" -lt "$current_value" ]; then
current_node=${__BINARYSEARCHTREE_INSTANCE_DATA__["$id", "$((current_node + 1))"]}
else
current_node=${__BINARYSEARCHTREE_INSTANCE_DATA__["$id", "$((current_node + 2))"]}
fi
if [ "$current_node" == '' ]; then
return 1
fi
done
else
return 1
fi;;
'.size' | '[size]' | "['size']" | '["size"]')
if [ "${__BINARYSEARCHTREE_INSTANCE_DATA__["$id", 0] + set}" ]; then
echo "${__BINARYSEARCHTREE_INSTANCE_DATA_SIZE__["$id"]}"
else
echo 0
fi;;
'.empty' | '[empty]' | "['empty']" | '["empty"]')
if [ "${__BINARYSEARCHTREE_INSTANCE_DATA__["$id", 0] + set}" ]; then
return 1
else
return 0
fi;;
'.clear' | '[clear]' | "['clear']" | '["clear"]')
unset "__BINARYSEARCHTREE_INSTANCE_DATA__[$id, 0]"
esac
}
And then make objects like this:
source './BinarySearchTree.sh'
process()
{
local tree="$1"
$tree.insert 52
$tree.insert -150
$tree.insert 42
if $tree.has 42; then
echo 'Has 42!'
else
echo 'Does not have 42!'
fi
$tree.clear
echo "Size: $($tree.size)"
}
main()
{
local tree=$(BinarySearchTree)
process "$tree"
}
main "$#" "$@"
The advantages of this method are that objects can be passed into other functions and there are no external file operations. Even though this may seem impractical, it actually makes Bash quite a nice language to work in since you can modularize your classes.
I found a way that you can actually use object in bash, but you need think out of box. If we treat object as files, we can support OOP in bash. You can define a model or even validation for that, and store your object in a file like this:
# /tmp/obj1
var1=val1
var2=val2
# /tmp/obj2
var1=val11
var2=val22
and you can directly read object values from these files and do whatever you want with them. The point is we dont use bash variable here, its just text processing.
Hope you or other bash lovers can work with this method!
If you do not care about the appearance, you can achieve object orientation in the following ways.
oop.sh ( Core script )
function Send()
{
local receiver=${2}
declare -n members=${receiver}
local selector=${1}
shift 2
${members[$selector]} ${receiver} ${*}
}
This is an example of using oop.sh to switch scp transfer direction.
example.sh
#!/usr/bin/bash
source oop.sh
# Push class member definition
function Push_Copy()
{
declare -n this=$1
shift
scp ${1} ${2}
}
# Push class definition
function Push()
{
declare -n this=${1}
this["Copy"]=Push_Copy
}
# Pull class member definition
function Pull_Copy()
{
declare -n this=$1
shift
scp ${2} ${1}
}
# Pull class definition
function Pull()
{
declare -n this=${1}
this["Copy"]=Pull_Copy
}
# Assets class member definition
function Assets_CopyBy()
{
declare -n this=${1}
local operation=${2}
# operation.Copy( "./file1.txt", "[email protected]:/home/user/file1.txt" )
Send Copy ${operation} "./file1.txt" "[email protected]:/home/user/file1.txt"
# operation.Copy( "./file2.txt", "[email protected]:/home/user/file2.txt" )
Send Copy ${operation} "./file2.txt" "[email protected]:/home/user/file2.txt"
}
# Assets class definition
function Assets()
{
declare -n this=${1}
this["CopyBy"]=Assets_CopyBy
}
declare -A global_operation
declare -A global_assets
# global_assets = Assets()
Assets global_assets
# global_operation = Pull()
Pull global_operation
# scp ./file1.txt [email protected]:/home/user/file1.txt
# scp ./file2.txt [email protected]:/home/user/file2.txt
# global_assets.CopyBy( global_operation )
Send CopyBy global_assets global_operation
# global_operation = Push()
Push global_operation
# scp [email protected]:/home/user/file1.txt ./file1.txt
# scp [email protected]:/home/user/file2.txt ./file2.txt
# global_assets.CopyBy( global_operation )
Send CopyBy global_assets global_operation
Succession required? If so, you can inherit in the following ways.
function Base_Name()
{
declare -n this=${1}
echo "Base"
}
function Base_Amount()
{
declare -n this=${1}
echo "0"
}
function Base()
{
declare -n this=${1}
this["Name"]=Base_Name
this["Amount"]=Base_Amount
}
function Derived_Name()
{
declare -n this=${1}
echo "Derived"
}
function Derived()
{
declare -n this=${1}
Base ${1} # Derived inherits from Base
this["Name"]=Derived_Name
}
declare -A global_object
Derived global_object
Send Name global_object # -> Derived
Send Amount global_object # -> 0
Base global_object
Send Name global_object # -> Base
Send Amount global_object # -> 0
© 2022 - 2024 — McMap. All rights reserved.
bash
is not an object-oriented language. – Oklahoma