Shell orienté objet pour * nix


38

Préface: J'adore Bash et n'ai aucune intention de déclencher une quelconque dispute ou une guerre sainte. J'espère que ce n'est pas une question extrêmement naïve.

Cette question est un peu liée à ce post sur le superutilisateur, mais je ne pense pas que l’opérateur savait vraiment ce qu’il demandait. J'utilise bash sous FreeBSD, Linux, OS X et cygwin sous Windows. J'ai également récemment eu une vaste expérience avec PowerShell sous Windows.

Existe-t-il un shell pour * nix, déjà disponible ou en préparation, compatible avec bash mais ajoutant une couche de script orienté objet dans le mélange? La seule chose que je sache qui se rapproche est la console python, mais pour autant que je sache, elle ne donne pas accès à l'environnement shell standard. Par exemple, je ne peux pas simplement cd ~et lsensuite chmod +x filedans la console Python. Je devrais utiliser python pour effectuer ces tâches plutôt que les fichiers binaires Unix standard, ou appeler les fichiers binaires à l'aide du code python.

Une telle coquille existe-t-elle?


3
Il y a Pash mais c'est beaucoup plus semblable à Powershell qu'à Bash.
éphémient

1
@ ephemient, vous devriez peut-être écrire une réponse pour pash ... même si je ne sais rien, iirc, powershell est un shell OO.
xenoterracide

4
Hey, vous devriez vérifier ipython . Si vous entrez une expression qui n'a pas de sens en tant que python, elle tentera de la mapper à une commande shell. Par exemple, des trucs comme cd ~suivi de lsfonctionne comme dans Bash. Vous pouvez également affecter une sortie aux variables Python (listes de lignes listing = !ls.
Intuition

@intuited: génial, je vais y jeter un coup d'œil
Robert S Ciaccio

1
@intuited: iPython a été très bon pour ce que je veux faire, merci!
Robert S Ciaccio

Réponses:


43

Je peux penser à trois caractéristiques souhaitables dans un shell:

  • Facilité d'utilisation interactive: les commandes courantes doivent être rapides à taper; achèvement; ...
  • Programmation: structures de données; accès simultanés (travaux, pipe, ...); ...
  • Accès au système: utilisation de fichiers, processus, fenêtres, bases de données, configuration du système, ...

Les shells Unix ont tendance à se concentrer sur l'aspect interactif et à sous-traiter la plupart des accès au système et une partie de la programmation à des outils externes, tels que:

  • bc pour les maths simples
  • openssl pour la cryptographie
  • sed , awk et autres pour le traitement de texte
  • nc pour la mise en réseau TCP / IP de base
  • ftp pour FTP
  • mail, Mail, mailx, Etc. pour le courrier électronique de base
  • cron pour les tâches planifiées
  • wmctrl pour la manipulation de base des fenêtres X
  • dcop pour les bibliothèques KDE ≤3.x
  • Outils dbus ( dbus-*ou qdbus ) pour diverses informations système et tâches de configuration (y compris les environnements de bureau modernes tels que KDE ≥4)

Beaucoup, beaucoup de choses peuvent être faites en appelant une commande avec les bons arguments ou une entrée canalisée. C'est une approche très puissante - mieux vaut avoir un outil par tâche qui le fait bien, qu'un seul programme qui fait tout sauf le mal - mais il a ses limites.

Une limitation majeure des shells Unix, et je suppose que c’est ce que vous voulez faire avec votre exigence de «script orienté objet», est qu’ils ne sont pas capables de conserver les informations d’une commande à l’autre, ni de les combiner de manière plus sophistiquée qu’elle ne le ferait. un pipeline. En particulier, la communication entre programmes étant basée sur du texte, les applications ne peuvent être combinées que si elles sérialisent leurs données de manière compatible. C’est à la fois une bénédiction et une malédiction: l’approche «tout ce qui est texte» permet d’accomplir rapidement des tâches simples, mais soulève la barrière des tâches plus complexes.

La facilité d'utilisation interactive va également plutôt à l'encontre de la maintenabilité du programme. Les programmes interactifs doivent être courts, demander peu de guillemets, ne pas vous gêner par des déclarations de variables ou des dactylographes, etc. est une chaîne, un nom de fonction, un nom de variable, etc.), doit avoir des contrôles de cohérence tels que les déclarations de variables et le typage, etc.

En résumé, un shell est un compromis difficile à atteindre. Ok, cela termine la section sur les exemples.


  • Le shell Perl (psh) «combine la nature interactive d’un shell Unix avec la puissance de Perl». Des commandes simples (même des pipelines) peuvent être entrées dans la syntaxe du shell; tout le reste est Perl. Le projet n'a pas été en développement depuis longtemps. C'est utilisable, mais je n'ai pas encore envisagé de l'utiliser au-dessus de pur Perl (pour les scripts) ou pur shell (de manière interactive ou pour les scripts).

  • IPython est une console Python interactive améliorée, particulièrement destinée à l'informatique numérique et parallèle. C'est un projet relativement jeune.

  • irb (interactive ruby) est l'équivalent Ruby de la console Python.

  • scsh est une implémentation de schéma (c'est-à-dire un langage de programmation décent) avec le type de liaisons système que l'on trouve traditionnellement dans les shells Unix (chaînes, processus, fichiers). Il ne vise toutefois pas à être utilisable en tant que shell interactif.

  • zsh est un shell interactif amélioré. Son point fort est l’interactivité (édition en ligne de commande, exécution, tâches courantes accomplies avec une syntaxe concise mais cryptique). Ses fonctionnalités de programmation ne sont pas très bonnes (à égalité avec ksh), mais il est livré avec un certain nombre de bibliothèques pour le contrôle de terminal, les expressions rationnelles, les réseaux, etc.

  • le poisson est un début propre à une coquille de style unix. Il n'a pas de meilleures fonctionnalités de programmation ou d'accès au système. Parce qu'il supprime la compatibilité avec sh, il offre davantage de possibilités pour développer de meilleures fonctionnalités, mais cela ne s'est pas produit.


Addendum: une autre partie de la boîte à outils unix traite beaucoup de choses comme des fichiers:

  • La plupart des périphériques matériels sont accessibles sous forme de fichiers.
  • Sous Linux, /sysfournit plus de contrôle du matériel et du système.
  • Sur de nombreuses variantes unix, le contrôle de processus peut être effectué via le /procsystème de fichiers.
  • FUSE facilite l'écriture de nouveaux systèmes de fichiers. Il existe déjà des systèmes de fichiers permettant de convertir des formats de fichiers à la volée, d'accéder à des fichiers via divers protocoles réseau, de consulter des archives, etc.

Peut-être que l'avenir des shells Unix ne sera pas un meilleur accès au système via des commandes (et de meilleures structures de contrôle pour combiner des commandes), mais un meilleur accès au système via des systèmes de fichiers (qui se combinent de manière légèrement différente - le shell pipe) sont encore).


1
Vous avez mis le doigt sur la tête juste après avoir dit "Je suppose que c'est ce que vous recherchez". La principale raison pour laquelle je pose cette question est que j'adore le pouvoir des outils Unix, mais l'interaction textuelle entre les programmes crée «un obstacle pour des tâches plus complexes». J'ai passé suffisamment de temps de programmation à écrire des analyseurs de texte :) Je pense que cette réponse est très bien pensée. Cela touche au cœur du problème et à la complexité du sujet. Je voudrais pouvoir le voter deux fois: P
Robert S Ciaccio

1
+1 pour ipython, bien que je n'aie aucune idée de ce que le PO veut faire.
Falmarri

1
C'est une excellente réponse: je pense honnêtement que les germes d'une thèse de doctorat intéressante se trouvent ici.
Ziggy

1
@RobertSCiaccio Cette réponse venait juste d'être liée dans un message récent, et votre commentaire sur l'analyse de texte m'a fait réfléchir ... si l'analyse de texte est suffisante pour accomplir vos "tâches complexes", ne pourriez-vous pas avoir un petit script ou programme qui implémente et l'utiliser comme une sorte de fonction dans vos scripts bash? Juste une pensée, je n'ai pas beaucoup d'expérience de script bash à ma ceinture pour parler de.
Oxwivi

1
@onlyanegg De quelle manière pourrait-on dire que le poisson est «orienté objet»? Poisson vise principalement à être plus simple. Y a-t-il une manière en quoi il est plus puissant que les alternatives?
Gilles 'SO- arrête d'être méchant'

13

Vous n'avez pas besoin de beaucoup de code bash pour implémenter des classes ou des objets dans bash.

Dis, 100 lignes.

Bash a des tableaux associatifs qui peuvent être utilisés pour implémenter un système Object simple avec héritage, méthodes et propriétés.

Donc, vous pourriez définir une classe comme celle-ci:

class Queue N=10 add=q_add remove=q_remove

La création d'une instance de cette file d'attente pourrait se faire comme suit:

class Q:Queue N=100

ou

inst Q:Queue N=100

Comme les classes sont implémentées avec un tableau, class et inst sont vraiment synonymes - un peu comme en javascript.

L'ajout d'éléments dans cette file d'attente pourrait se faire comme suit:

$Q add 1 2 aaa bbb "a string"

Supprimer des éléments dans une variable X pourrait se faire comme ceci:

$Q remove X

Et la structure de déchargement d'un objet pourrait être faite comme ceci:

$Q dump

Ce qui retournerait quelque chose comme ceci:

Q {
      parent=Queue {
                     parent=ROOT {
                                   this=ROOT
                                   0=dispatch ROOT
                                 }
                     class=Queue
                     N=10
                     add=q_add
                     remove=q_remove
                     0=dispatch Queue
                   }
      class=Q
      N=4
      add=q_add
      remove=q_remove
      0=dispatch Q
      1=
      2=ccc ddd
      3=
      4=
    }

Les classes sont créées en utilisant une fonction de classe comme ceci:

class(){
    local _name="$1:"                            # append a : to handle case of class with no parent
    printf "$FUNCNAME: %s\n" $_name
    local _this _parent _p _key _val _members
    _this=${_name%%:*}                           # get class name
    _parent=${_name#*:}                          # get parent class name
    _parent=${_parent/:/}                        # remove handy :
    declare -g -A $_this                         # make class storage
    [[ -n $_parent ]] && {                       # copy parent class members into this class
        eval _members=\"\${!$_parent[*]}\"       # get indices of members
        for _key in $_members; do                # inherit members from parent
            eval _val=\"\${$_parent[$_key]}\"    # get parent value
            eval $_this[$_key]=\"$_val\"         # set this member
        done
    }
    shift 1

    # overwrite with specific values for this object
    ROOT_set $_this "$@" "0=dispatch $_this" "parent=${_parent:-ROOT}" "class=$_this"
}

REMARQUE: lors de la définition d'une nouvelle classe ou instance, vous pouvez remplacer toute valeur ou fonction de membre.

Les tableaux associatifs Bash ont une bizarrerie qui rend ce travail parfaitement ordonné: $ Q [0]} est identique à $ Q. Cela signifie que nous pouvons utiliser le nom du tableau pour appeler une fonction d'envoi de méthode:

dispatch(){
    local _this=$1 _method=$2 _fn
    shift 2
    _fn="$_this[$_method]"                       # reference to method name
    ${!_fn} $_this "$@"
}

Un inconvénient est que je ne peux pas utiliser [0] pour les données, donc mes files d'attente (dans ce cas) commencent à index = 1. Sinon, j'aurais pu utiliser des indices associatifs tels que "q + 0".

Pour obtenir et définir des membres, vous pouvez faire quelque chose comme ceci:

# basic set and get for key-value members
ROOT_set(){                                       # $QOBJ set key=value
    local _this=$1 _exp _key _val
    shift
    for _exp in "$@"; do
        _key=${_exp%%=*}
        _val="${_exp#*=}"
        eval $_this[$_key]=\"$_val\"
    done
}

ROOT_get(){                                       # $QOBJ get var=key
    local _this=$1 _exp _var _key
    shift
    for _exp in "$@"; do
        _var=${_exp%%=*}
        _key=${_exp#*=}
        eval $_var=\"\${$_this[$_key]}\"
    done
}

Et pour vider une structure d'objet, j'ai fait ceci:

NOTE: Ceci n'est pas nécessaire pour la programmation orientée objet dans bash, mais il est agréable de voir comment les objets sont créés.

# dump any object
obj_dump(){                                      # obj_dump <object/class name>
    local _this=$1 _j _val _key; local -i _tab=${2:-(${#_this}+2)}  # add 2 for " {"
    _tab+=2                                      # hanging indent from {
    printf "%s {\n" $_this
    eval "_key=\"\${!$_this[*]}\""
    for _j in $_key; do                          # print all members
        eval "_val=\"\${$_this[\$_j]}\""
        case $_j in
            # special treatment for parent
            parent) printf "%*s%s=" $_tab "" $_j; ${!_val} dump $(( _tab+${#_j}+${#_val}+2 ));;
                 *) printf "%*s%s=%s\n" $_tab "" $_j "$_val";;
        esac
    done
    (( _tab-=2 ))
    printf "%*s}\n" $_tab ""
    return 0
}

Ma conception POO n'a pas pris en compte les objets à l'intérieur des objets, à l'exception de la classe héritée. Vous pouvez les créer séparément ou créer un constructeur spécial tel que class (). * obj_dump * devrait être modifié pour détecter les classes internes afin de les imprimer de manière récursive.

Oh! et je définis manuellement une classe ROOT pour simplifier la fonction de la classe :

declare -gA ROOT=(    \
  [this]=ROOT         \
  [0]="dispatch ROOT" \
  [dump]=obj_dump     \
  [set]="ROOT_set"    \
  [get]="ROOT_get"    \
)

Avec quelques fonctions de file d’attente, j'ai défini des classes comme celle-ci:

class Queue          \
    in=0 out=0 N=10  \
    dump=obj_dump    \
    add=q_add        \
    empty=q_empty    \
    full=q_full      \
    peek=q_peek      \
    remove=q_remove

class RoughQueue:Queue     \
    N=100                  \
    shove=q_shove          \
    head_drop=q_head_drop

Créé des instances de file d'attente et les a fait fonctionner:

class Q:Queue N=1000
$Q add aaa bbb "ccc ddd"
$Q peek X
$Q remove X
printf "X=%s\n" "$X"
$Q remove X
printf "X=%s\n" "$X"
$Q remove X
printf "X=%s\n" "$X"


class R:RoughQueue N=3
$R shove aa bb cc dd ee ff gg hh ii jj
$R dump

Ça pourrait marcher mais c'est moche . Et totalement pas ce qui bashest pour. Cela me rappelle la réponse de Stéphane à propos de pourquoi ne pas utiliser des boucles de shell pour traiter du texte, en particulier la section intitulée "conceptuellement" qui détaille la différence de finalité entre les langages tels que C et bash. unix.stackexchange.com/a/169765/135943
Wildcard

1
Pourrait fonctionner? Cela fonctionne, mais vous dites que, même si ce n’est pas faux, cela n’est tout simplement pas fait. Je n'ai pas non plus répondu à la question OP, mais si bash est TC, je pensais qu'il devrait pouvoir traiter des objets. Et beaucoup l'ont démontré.
philcolbourn


5

IPython est étonnamment pratique à utiliser.

Fonctionnalités standard du shell: contrôle des travaux, édition et historique de readline, alias cat ls cdet pwdintégration du pager, exécution de toute commande système en la préfixant avec une sortie !ou en activant la %rehashxsortie assignable à une variable python, valeurs python disponibles en tant que variables shell.

Spécifique à Python: réutilisation des résultats des dernières commandes, accès rapide à la documentation et aux sources, rechargement du module, débogueur. Un certain soutien de cluster si vous êtes dans cela.

Cela dit, faire fonctionner des pipes complexes ne se fait pas en Python; vous utiliserez également le shell posix, juste avec de la colle pour transmettre des valeurs.



2

jq fonctionne assez bien comme une telle sorte de couche orientée objet.


2

Celui-ci est un peu plus simple à utiliser et à configurer, a nommé args, etc. https://github.com/uudruid74/bashTheObjects

Je mets à jour ma réponse avec un exemple, qui suit l'un des exemples de base donnés pour une autre réponse, mais avec cette syntaxe. Le programme d'exemple est similaire, mais vous n'avez pas besoin de préfixer toutes les variables avec le nom de classe (il le sait comme le montre la méthode kindof ) et je pense que la syntaxe est beaucoup plus simple!

Tout d'abord, un fichier de classe. Les valeurs par défaut des variables d'instance sont facultatives et ne sont utilisées que si vous ne transmettez pas ces valeurs au constructeur.

class Person
    public show
    public set
    public Name
    public Age
    public Sex
    inst var Name "Saranyan"
    inst var Age 10
    inst var Sex "Male"

Person::Person { :; }
Person::set() { :; }
Person::Name() { println $Name }
Person::Age() { println $Age }
Person::Sex() { println $Sex }
Person::show() {
    Person::Name
    Person::Age
    Person::Sex
}

Maintenant, par exemple l'utilisation:

#!/bin/bash
source static/oop.lib.sh

import Person

new Person Christy Name:"Christy" Age:21 Sex:"female"
new Person Evan Name:"Evan" Age:41 Sex:"male"

println "$(Evan.Name) is a $(Evan.Sex) aged $(Evan.Age)"
println "$(Christy.Name) is a $(Christy.Sex) aged $(Christy.Age)"
println "Stats for Evan ..."
Evan.show

assert 'kindof Person Evan'
assert '[ $Evan = $Evan ]'
assert 'kindof Person Christy'
assert '[ $Evan = $Christy ]'

REMARQUES:

  1. Cette dernière assertion échouera. Contrairement à l'exemple ci-dessus, la bibliothèque ne prend pas encore en charge l'affectation d'objets, mais cela ne serait pas trop difficile à ajouter. Je le placerai sur mon TO-DO avec le prochain support de conteneur / itérateur.

L'instruction d'importation n'est pas requise techniquement, mais elle force le chargement de la classe à un point donné au lieu d'attendre la première nouvelle , ce qui peut aider à initialiser les éléments dans le bon ordre. Notez la facilité avec laquelle vous pouvez définir plusieurs variables d'instance en même temps.

Il existe également des niveaux de débogage, des constructeurs, des destructeurs, des sous - classes et un système de réflexion de base . L'option print / println remplacera echo (essayez-vous d'imprimer une variable commençant par un tiret?). L'exemple sur github montre qu'il s'exécute en tant que CGI générant du HTML à partir de classes.

La bibliothèque elle-même (oop.lib.sh) n’est pas si simple (plus de 400 lignes, 11K), mais vous devez l’inclure et l’oublier.



1

Si quelqu'un ne souhaite que les bases de la programmation orientée objet (propriétés et méthodes), un simple framework suffirait.

Supposons que vous souhaitiez afficher le texte "Hello World" à l'aide d'objets. Tout d'abord, vous créez une classe d'objet qui a une propriété pour le texte à afficher et des méthodes pour définir ce texte et l'afficher. Pour montrer comment plusieurs instances d'une classe peuvent fonctionner ensemble, j'ai ajouté deux méthodes pour afficher le texte: une avec NewLine à la fin et l'autre sans cela.

Fichier de définition de classe: EchoClass.class

# Define properties
<<InstanceName>>_EchoString="Default text for <<InstanceName>>"

# Define methods
function <<InstanceName>>_SetEchoString()
{
  <<InstanceName>>_EchoString=$1
}

function <<InstanceName>>_Echo()
{
  # The -ne parameter tells echo not to add a NewLine at the end (No Enter)
  echo -ne "$<<InstanceName>>_EchoString"
}

function <<InstanceName>>_EchoNL()
{
  echo "$<<InstanceName>>_EchoString"
}

Veuillez noter le mot "<<InstanceName>>". Ceci sera remplacé plus tard pour créer plusieurs instances d'un objet de classe. Avant de pouvoir utiliser une instance d'objet, vous avez besoin d'une fonction qui le crée réellement. Pour que les choses restent simples, il s'agira d'un script séparé appelé: ObjectFramework.lib

# 1st parameter : object instance name
# 2nd parameter : object instance class

function CreateObject()
{
  local InstanceName=$1
  local ObjectClass=$2
  # We will replace all occurences of the text "<<InstanceName>>" in the class file 
  # to the value of the InstanceName variable and store it in a temporary file
  local SedString='s/<<InstanceName>>/'$InstanceName'/g '$ObjectClass'.class'
  local TmpFile=$ObjectClass'_'$InstanceName'.tmp'
  sed $SedString > $TmpFile

  # The file will contain code which defines variables (properties) and functions (methods)
  # with the name we gave to our object instance via the 1st parameter of this function
  # ... we run this code so the variables and functions are actually defined in runtime
  source "$TmpFile"

  # Than remove the temp file as we don't need it any more
  rm "$TmpFile"
}

Nous avons maintenant un fichier de définition de classe et une fonction CreateObject qui crée une copie de ce fichier avec le texte "<<NomInstance>>" remplacé par le nom de votre choix.

Utilisons notre nouvel objet dans un script appelé: HelloWorld.sh (veuillez noter que HelloWorld.sh doit être exécutable. Les deux autres fichiers ne sont pas nécessaires).

# Define the CreateObject function via the lib file we created
source ObjectFramework.lib

# Create two instances of the EchoClass class
CreateObject MyHello EchoClass
CreateObject MyWorld EchoClass

# Call the SetEchoString method of the two objects. In reality these are 
# just two identical functions named differently and setting different
# variables (remember the <<InstanceName>>_EchoString variable?)
MyHello_SetEchoString "Hello "
MyWorld_SetEchoString "World"

# Finally we call the Echo and EchoNL (NewLine) methods
MyHello_Echo
MyWorld_EchoNL

En exécutant le script HelloWorld.sh, il affiche le texte "Hello World" (et ajoute une NewLine). Personne ne sera impressionné par ce résultat, mais nous saurons que ce n’est pas aussi simple que ça en a l'air :)

Bonne codage!


Mieux vaut simplifier les choses complexes que complexes.
Wildcard

1

Il s’agit d’un shell orienté objet basé sur Python, mais dont la taille est proche de Golang: https://github.com/alexst07/shell-plus-plus

Par exemple, essayez catch:

try {
  git clone git@github.com:alexst07/shell-plus-plus.git
} catch InvalidCmdException as ex {
  print("git not installed [msg: ", ex, "]")
}

surcharge de classe et d'opérateur:

class Complex {
  func __init__(r, i) {
    this.r = r
    this.i = i
  }

  func __add__(n) {
    return Complex(n.r + this.r, n.i + this.i)
  }

  func __sub__(n) {
    return Complex(n.r - this.r, n.i - this.i)
  }

  func __print__() {
    return string(this.r) + " + " + string(this.i) + "i"
  }
}

c1 = Complex(2, 3)
c2 = Complex(1, 2)
c = c1 + c2

print(c)

et vous pouvez utiliser les commandes similaires bash:

echo "Test" | cat # simple pipeline
ls src* | grep -e "test" # using glob

# using variables content as command
cip = "ipconfig"
cgrep = ["grep", "-e", "10\..*"]
${cip} | $@{cgrep} # pass an array to command

0

Maintenant, avec quels objets traitez-vous dans un shell la plupart du temps? Ce sont des fichiers / répertoires, des processus et leurs interactions. Donc, il devrait aimer f1.editou quelque chose comme currentFile=f1.c ; .edit ; .compile ; .run. Ou d1.search(filename='*.c' string='int \*'). Ou p1.stop, p1.bg. C'est ma compréhension d'un ooshell.


0
## implemantion of base class
function Class()
{
    base=${FUNCNAME}
    this=${1}
    Class_setCUUID $this
    for method in $(compgen -A function)
    do
        export ${method/#$base\_/$this\_}="${method} ${this}"
    done

}

function copyCUUID()
{
        export ${2}_CUUID=$(echo $(eval "echo \$${1}_CUUID"))

}

function Class_setCUUID()
{
        export ${1}_CUUID=$(uuid)
}

function Class_getCUUID()
{
        echo $(eval "echo \$${2}_CUUID")
}


function Class_setProperty()
{
        export ${1}_${2}=${3}
}

function Class_getProperty()
{
        echo $(eval "echo \$${1}_${2}")
}

function Class_Method()
{
        echo "function ${1}_${2}()
        {
        echo null
        }
        " > /tmp/t.func
        . /tmp/t.func
        rm /tmp/t.func


}

function Class_setMethod()
{
        export ${1}_${2}=${1}_${2}
}


function Class_getMethod()
{
        $(eval "echo \$${1}_${2}")
}


function Class_equals()
{
        base="Class"
        this=${2}

    copyCUUID ${1} ${2}
    for method in $(compgen -A function)
    do
        export ${method/#$base\_/$this\_}="${method} ${1}"
    done


}

je viens d'essayer d'introduire des concepts oo à bash basés sur la référence http://hipersayanx.blogspot.in/2012/12/object-oriented-programming-in-bash.html

source ./oobash

Class person
$person_setProperty Name "Saranyan"
$person_setProperty Age 10
$person_setProperty Sex "Male"
function person_show()
{
$person_getProperty Name
$person_getProperty Age
$person_getProperty Sex
}
$person_setMethod show

$person_equals person1
$person1_getMethod show
$person1_equals person3
$person_getCUUID person
$person_getCUUID person1
$person_getCUUID person3

0

Désolé pour la réponse courte, mais voilà.

hipersayanx a créé un article sur la programmation orientée objet dans Bash . Au fond , il salut-Jacked $FUNCNAME, function, compgenet exportde créer aussi près de POO on peut obtenir en bash.

La partie cool est que cela fonctionne bien et il suffit de quelques lignes de passe-partout pour construire une classe.

Les pièces de base nécessaires sont:

ClassName() {
# A pointer to this Class. (2)
base=$FUNCNAME
this=$1

# Inherited classes (optional).
export ${this}_inherits="Class1 Class2 Class3" # (3.1)
 for class in $(eval "echo \$${this}_inherits")
do
    for property in $(compgen -A variable ${class}_)
    do
        export ${property/#$class\_/$this\_}="${property}" # (3.2)
    done

    for method in $(compgen -A function ${class}_)
    do
        export ${method/#$class\_/$this\_}="${method} ${this}"
    done
done

# Declare Properties.
export ${this}_x=$2
export ${this}_y=$3
export ${this}_z=$4

# Declare methods.
for method in $(compgen -A function); do
    export ${method/#$base\_/$this\_}="${method} ${this}"
done
}

function ClassName_MethodName()
{
#base is where the magic happens, its what holds the class name
base=$(expr "$FUNCNAME" : '\([a-zA-Z][a-zA-Z0-9]*\)')
this=$1

x=$(eval "echo \$${this}_x")

echo "$this ($x)"
}

Usage:

# Create a new Class Instance
ClassName 'instanceName' $param1 $param2

$instanceName_method

Maintenant, je l'ai moi-même utilisé dans mon projet AuditOps et hipersayanx a plus de détails sur la façon dont cela fonctionne réellement sur son site. L'avertissement de tarif, bien qu'il s'agisse d'un basisme, ne fonctionnera avec aucune version antérieure à bash 4.0 et peut entraîner des maux de tête lors du débogage. Personnellement, j'aimerais voir la plupart des plaques de chaudière refaites en tant que classe.

Il est toujours plus sage d'utiliser un langage de script POO sérieux, tel que perl, ruby ​​et python, lorsqu'il convient mieux à votre projet. Cependant, dans mon honnête option, il vaut la peine de consacrer du temps et des efforts à la maintenance de scripts bash modulaires pour utiliser cette méthode de POO en bash.


0

Je développe sur GitHub une fonction qui fonctionne comme un objet HashMap , shell_map .

Afin de créer des " instances HashMap ", cette fonction peut créer des copies de lui-même sous différents noms. Chaque nouvelle copie de fonction aura une variable différente $ FUNCNAME. $ FUNCNAME est ensuite utilisé pour créer un espace de noms pour chaque instance de Map.

Les clés de la carte sont des variables globales, sous la forme $ FUNCNAME_DATA_ $ KEY, où $ KEY est la clé ajoutée à la carte. Ces variables sont des variables dynamiques .

Ci-dessous, je vais en mettre une version simplifiée pour que vous puissiez vous en servir comme exemple.

#!/bin/bash

shell_map () {
    local METHOD="$1"

    case $METHOD in
    new)
        local NEW_MAP="$2"

        # loads shell_map function declaration
        test -n "$(declare -f shell_map)" || return

        # declares in the Global Scope a copy of shell_map, under a new name.
        eval "${_/shell_map/$2}"
    ;;
    put)
        local KEY="$2"  
        local VALUE="$3"

        # declares a variable in the global scope
        eval ${FUNCNAME}_DATA_${KEY}='$VALUE'
    ;;
    get)
        local KEY="$2"
        local VALUE="${FUNCNAME}_DATA_${KEY}"
        echo "${!VALUE}"
    ;;
    keys)
        declare | grep -Po "(?<=${FUNCNAME}_DATA_)\w+((?=\=))"
    ;;
    name)
        echo $FUNCNAME
    ;;
    contains_key)
        local KEY="$2"
        compgen -v ${FUNCNAME}_DATA_${KEY} > /dev/null && return 0 || return 1
    ;;
    clear_all)
        while read var; do  
            unset $var
        done < <(compgen -v ${FUNCNAME}_DATA_)
    ;;
    remove)
        local KEY="$2"
        unset ${FUNCNAME}_DATA_${KEY}
    ;;
    size)
        compgen -v ${FUNCNAME}_DATA_${KEY} | wc -l
    ;;
    *)
        echo "unsupported operation '$1'."
        return 1
    ;;
    esac
}

Usage:

shell_map new credit
credit put Mary 100
credit put John 200
for customer in `credit keys`; do 
    value=`credit get $customer`       
    echo "customer $customer has $value"
done
credit contains "Mary" && echo "Mary has credit!"

Vous semblez avoir mal compris le fonctionnement de la substitution de commande. Il est remplacé par la sortie de la commande contenue dans les backticks, non remplacée par le statut de retour . En d'autres termes, votre code ne fait pas ce que vous pensez qu'il fait.
Wildcard

Aha. Vous avez raison. Mes excuses. Cependant, il serait beaucoup plus clair d'utiliser à la carp "Some error message"; returnplace.
Wildcard

Ou [ -z "$KEY" ] && { carp "some message"; return;} pas besoin d'un sous-shell. Mais en réalité, cela ressemble à un candidat réel pour une if ... elif ... elif ... else ... ficonstruction - ce qui n'est pas souvent le meilleur choix, mais l'est probablement. :) (Ou peut-être un changement de cas.)
Wildcard

@Wildcard j'ai édité la réponse. Maintenant, notre discussion sur les carpes et les butées n'a plus aucun sens. effaçons cette conversation?
Bruno Negrão Zica

0

Plumbum est un langage shell semblable à Python. Il empaquette shell comme syntaxe avec Python rendant l'expérience orientée objet.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.