Об'єктно-орієнтована оболонка для * nix


38

Передмова: Я люблю баш та не маю наміру починати будь-які спори чи священну війну, і, сподіваюся, це не надзвичайно наївне питання.

Це питання дещо пов'язане з цією посадою про суперпользователя, але я не думаю, що ОП дійсно знала, про що він просить. Я використовую bash на FreeBSD, linux, OS X та cygwin у Windows. Я також мав великий досвід роботи з PowerShell в Windows.

Чи є оболонка для * nix, вже доступна або в роботі, сумісна з bash, але додає шар об'єктно-орієнтованого сценарію в суміш? Єдине, що мені відомо про це, це близько - консоль python, але, наскільки я можу сказати, вона не забезпечує доступ до стандартного середовища оболонки. Наприклад, я не можу просто, cd ~а lsпотім chmod +x fileвсередині консолі пітона. Мені доведеться використовувати python для виконання цих завдань, а не стандартні бінарні файли Unix, або викликати бінарні файли, використовуючи код python.

Чи існує така оболонка?


3
Там Pash, але це набагато більше Powershell, ніж Bash.
ефемієнт

1
@ephemient, можливо, ви повинні написати відповідь на паш ... Хоча я нічого не знаю, але це, iirc, powerhell - оболонка OO.
ксенотеррацид

4
Гей, ви повинні перевірити ipython . Якщо ви введете вираз, який не має сенсу як python, він спробує відобразити його на команду shell. Наприклад, такі речі, як cd ~слідом за lsтворами, як у Bash. Ви також можете призначити вихід змінним Python (списки рядків. Роду) за допомогою таких команд listing = !ls.
інтуїтивно

@intuited: приголомшливо, я перевірю це
Роберт S Ciaccio

1
@intuited: iPython був дуже гарним для тих речей, які я хочу зробити, дякую!
Robert S Ciaccio

Відповіді:


43

Я можу придумати три бажані функції в оболонці:

  • Інтерактивна юзабіліті: загальні команди повинні швидко набирати; завершення; ...
  • Програмування: структури даних; паралельність (робочі місця, труба, ...); ...
  • Доступ до системи: робота з файлами, процесами, Windows, базами даних, конфігурація системи, ...

Оболонки Unix, як правило, зосереджуються на інтерактивному аспекті та надають субпідрядку більшість системного доступу та частину програмування до зовнішніх інструментів, таких як:

  • Bc для простої математики
  • openssl для криптографії
  • sed , awk та інші для обробки тексту
  • nc для базових мереж TCP / IP
  • ftp для FTP
  • mail, Mail, mailxІ т.д. для основної електронної пошти
  • cron для планових завдань
  • wmctrl для основної маніпуляції з вікнами X
  • dcop для бібліотек KDE ≤3.x
  • інструменти dbus ( dbus-*або qdbus ) для різних системних завдань та конфігурації (включаючи сучасні середовища на робочому столі, такі як KDE ≥4)

Багато, багато речей можна зробити, викликавши команду з правильними аргументами або трубопровідним введенням. Це дуже потужний підхід - краще мати один інструмент на кожне завдання, який виконує його добре, ніж одну програму, яка робить все, але погано - але у неї є свої обмеження.

Основне обмеження оболонок Unix, і я підозрюю, що це те, що ви дотримуєтесь з вимогою "об'єктно-орієнтованого сценарію", полягає в тому, що вони не вміють зберігати інформацію від однієї команди до іншої, або комбінувати команди в команді більш привабливими, ніж трубопровід. Зокрема, міжпрограмна комунікація є текстовою, тому програми можна комбінувати лише у тому випадку, якщо вони послідовно передають свої дані сумісним чином. Це і благо, і прокляття: підхід "все-що є текст" дозволяє легко виконувати прості завдання швидко, але піднімає бар'єр для складніших завдань.

Інтерактивна зручність використання також суперечить технічному обслуговуванню програми. Інтерактивні програми повинні бути короткими, вимагати невеликого котирування, не обтяжувати вас змінними деклараціями чи введеннями тексту тощо. Обслуговувані програми повинні бути читабельними (тому не мати багато абревіатур), читатими (так що вам не доведеться замислюватися, чи голе слово це рядок, ім'я функції, ім'я змінної тощо), повинні мати перевірку узгодженості, такі як оголошення змінних та введення тексту тощо.

Підводячи підсумок, до оболонки важко дійти компромісу. Гаразд, цим закінчується розділ, що проводився, на прикладах.


  • Perl Shell (PSH) «поєднує в собі інтерактивну природу Unix оболонки з силою Perl». Прості команди (навіть трубопроводи) можуть бути введені в синтаксис оболонки; все інше - Perl. Проект вже давно не розробляється. Це зручно, але не дійшло до того, що я б розглядав можливість використовувати його через чистий Perl (для сценаріїв) або чисту оболонку (інтерактивно чи для сценаріїв).

  • IPython - це вдосконалена інтерактивна консоль Python, особливо орієнтована на числові та паралельні обчислення. Це порівняно молодий проект.

  • irb (інтерактивний рубін) - еквівалент Ruby консолі Python.

  • scsh - це реалізація схеми (тобто пристойної мови програмування) з типом системних прив'язок, які традиційно зустрічаються в оболонках Unix (рядки, процеси, файли). Однак він не прагне бути корисним як інтерактивна оболонка.

  • zsh - вдосконалена інтерактивна оболонка. Його сильною стороною є інтерактивність (видання командного рядка, завершення, спільні завдання, виконані з короткою, але криптованою синтаксисом). Його функції програмування не такі вже й великі (нарівні з ksh), але він постачається з низкою бібліотек для управління терміналами, регулярними виразками, мережевими роботами тощо.

  • риба - це чистий початок в оболонці в стилі Unix. Він не має кращих функцій програмування та доступу до системи. Оскільки це порушує сумісність із sh, у нього є більше можливостей для розвитку кращих функцій, але цього не сталося.


Додаток: інша частина інструментальної панелі unix багато речей розглядає як файли:

  • Більшість апаратних пристроїв доступні у вигляді файлів.
  • У Linux /sysпередбачено більше апаратного та системного контролю.
  • У багатьох варіантах unix управління процесом може здійснюватися через /procфайлову систему.
  • FUSE дозволяє легко писати нові файлові системи. Вже є існуючі файлові системи для перетворення форматів файлів на ходу, доступу до файлів через різні мережеві протоколи, огляду всередині архівів тощо.

Можливо, майбутні оболонки Unix - це не кращий доступ до системи за допомогою команд (і краща структура управління для комбінування команд), а кращий доступ до системи через файлові системи (які поєднуються дещо інакше - я не думаю, що ми розробили які ключові ідіоми (як оболонка) поки що).


1
Ви потрапляєте в цвях по голові одразу після того, як ви говорите "Я підозрюю, що це ти за чим". Основна причина, яку я задаю цьому питанню, полягає в тому, що я люблю мати інструменти unix, але текстова взаємодія між програмами, безумовно, "створює бар'єр для складніших завдань". Я витратив достатньо своїх програмних днів на написання текстових аналізаторів :) Я думаю, що це дуже добре продумана відповідь. Це потрапляє до суті питання та складності теми. Я хотів би, щоб я міг підняти це двічі: Р
Роберт С. Ціаксіо

1
+1 для ipython, хоча я поняття не маю, що хоче зробити ОП.
Фальмарі

1
Це чудова відповідь: я чесно думаю, що насіння цікавої кандидатської дисертації є тут.
Ziggy

1
@RobertSCiaccio Цю відповідь якраз пов'язували в нещодавньому дописі, і ваш коментар щодо текстових розборів змусив мене задуматися ... якщо розбору тексту буде достатньо для виконання ваших "складних завдань", тоді ви не могли б мати невеликий сценарій чи програму, яка реалізує це і використовувати його як якусь функцію у ваших скриптах bash? Лише думка, я не маю особливого досвіду сценаріїв під своїм поясом.
Оксвіві

1
@onlyanegg Яким способом можна сказати, що риба є "об'єктно-орієнтована"? Риба насамперед спрямована на те, щоб бути простішою. Чи є якийсь спосіб, який він сильніший за альтернативи?
Жил "ТАК - перестань бути злим"

13

Вам не потрібно багато bash-коду для реалізації класів або об'єктів у bash.

Скажіть, 100 рядків.

Bash має асоціативні масиви, які можна використовувати для реалізації простої системи об'єктів з успадкуванням, методами та властивостями.

Отже, ви могли б визначити такий клас:

class Queue N=10 add=q_add remove=q_remove

Створення примірника цієї черги можна зробити так:

class Q:Queue N=100

або

inst Q:Queue N=100

Оскільки класи реалізовані з масивом, клас та інст справді є синонімами - подібними до javascript.

Додавання елементів у цю чергу можна зробити так:

$Q add 1 2 aaa bbb "a string"

Видалення елементів у змінну X може бути зроблено так:

$Q remove X

І демпінгову структуру об'єкта можна зробити так:

$Q dump

Що б повернуло щось подібне:

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=
    }

Класи створюються за допомогою функції класу на зразок цієї:

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"
}

ПРИМІТКА. Під час визначення нового класу чи екземпляра ви можете переосмислити значення або функції будь-якого члена.

Асоціативні масиви Bash мають вигадку, завдяки якій ця робота є акуратною: $ Q [0]} ідентичний $ Q. Це означає, що ми можемо використовувати ім'я масиву для виклику функції відправки методу:

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

Нижньою стороною є те, що я не можу використовувати [0] для даних, тому мої черги (в даному випадку) починаються з індексу = 1. Крім того, я міг би використовувати асоціативні індекси типу "q + 0".

Щоб отримати та встановити членів, ви можете зробити щось подібне:

# 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
}

І щоб скинути структуру об'єкта, я зробив це:

ПРИМІТКА. Це не потрібно для OOP в bash, але приємно бачити, як створюються об'єкти.

# 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
}

Моя конструкція OOP не враховувала об'єкти всередині об'єктів - за винятком спадкового класу. Ви можете створити їх окремо або зробити спеціальний конструктор на зразок class (). * obj_dump * потрібно буде модифікувати для виявлення внутрішніх класів для їх рекурсивного друку.

Ой! і я вручну визначаю клас ROOT для спрощення функції класу :

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

За допомогою декількох функцій черги я визначив такі класи, як цей:

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

Створив кілька екземплярів черги та змусив їх працювати:

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

Може працювати, але це некрасиво . І зовсім не для чого bash. Нагадує мені відповідь Стефана про те, чому б не використовувати петлі оболонки для обробки тексту, особливо розділ, очолюваний "концептуально", який детально розглядає різницю у цілях між мовами, такими як C та bash. unix.stackexchange.com/a/169765/135943
Wildcard

1
Може працювати? Це справді працює, але ваш пункт полягає в тому, що, хоча і не помиляється, це просто не робиться. Я також не відповідав на питання ОП, але якщо bash - це TC, то я вважав, що він повинен мати можливість обробляти об'єкти. І багато хто це продемонстрував.
philcolbourn


5

IPython напрочуд зручний у використанні.

Стандартні функції оболонки: управління роботою, редагування читання рядків і історія, псевдоніми cat ls cdта pwdінтеграція пейджера, виконання будь-якої системної команди за допомогою префіксації за допомогою !або включення %rehashx, виведення команд, що можна віднести до змінної python, значення python, доступні як змінні оболонки.

Специфічний для Python: повторне використання результатів останніх команд, швидкий доступ до документації та джерела, перезавантаження модулів, налагодження. Якась підтримка кластера, якщо ви займаєтесь цим.

Але це означає, що в Python запуск складних труб не робиться; ви також будете використовувати оболонку posix, просто за допомогою клею, щоб передати значення туди-сюди.



2

jq працює досить добре, оскільки такий тип об'єктно-орієнтованого шару.


2

Цей дещо простіший у використанні та налаштуваннях, має ім'я args тощо. Https://github.com/uudruid74/bashTheObjects

Я оновлюю свою відповідь прикладом, який випливає з одного з основних прикладів, поданих для іншої відповіді, але з цим синтаксисом. Приклад програми схожий, але вам не потрібно префіксувати всі змінні з іменем класу (він це знає, як показує метод kindof ), і я думаю, що синтаксис набагато простіший!

Спочатку файл класу. Значення за замовчуванням для змінних екземплярів є необов'язковими і використовуються лише у тому випадку, якщо ви не передаєте ці значення в конструктор.

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
}

Тепер, наприклад, використання:

#!/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 ]'

ПРИМІТКИ:

  1. Останнє твердження провалиться. На відміну від вищенаведеного прикладу, бібліотека ще не підтримує призначення об'єктів, але це було б не надто важко додати. Я розміщую його на своєму TO-DO разом із майбутнім підтримкою контейнерів / ітераторів.

Заява про імпорт технічно не потрібна, але вона змушує завантаження класу в заданій точці замість того, щоб чекати першого нового , що може допомогти ініціалізувати речі в належному порядку. Зверніть увагу на простоту, за допомогою якої ви можете встановити кілька змінних екземплярів одночасно.

Існують також рівні налагодження, конструктори, деструктори, підкласифікація та основна система відображення , а показано print / println замінити ехо (будь-коли спробувати надрукувати змінну, яка починається з тире?). Приклад на github показує, що він працює як CGI, що генерує HTML з класів.

Сама бібліотека (oop.lib.sh) не така проста (400+ рядків, 11K), але ви просто включите її і забудете.



1

Якщо хтось хоче лише основи об'єктно-орієнтованого програмування (властивості та методи), то справді простий фреймворк зробив би свою справу.

Скажімо, ви хочете відобразити текст "Hello World" за допомогою об'єктів. Спочатку ви створюєте клас об'єктів, який має властивість для відображення тексту, і має деякі способи встановлення цього тексту та його відображення. Щоб показати, як кілька примірників класу можуть працювати разом, я додав два способи відображення тексту: один із NewLine в кінці та один без цього.

Файл визначення класу: 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"
}

Зверніть увагу на слово "<<InstanceName>>". Це буде замінено пізніше, щоб створити кілька екземплярів об’єкта класу. Перш ніж використовувати екземпляр об'єкта, вам потрібна функція, яка фактично його створює. Щоб все було просто, це буде окремий сценарій під назвою: 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"
}

Отже, у нас є файл визначення класу та функція CreateObject, яка створює копію цього файлу з текстом "<<InstanceName>>", заміненим на будь-яке ім'я.

Давайте використаємо наш новий об’єкт у сценарії під назвою: HelloWorld.sh (зауважте, що HelloWorld.sh має бути виконаним. Інші два файли не потрібно)

# 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

Запустивши сценарій HelloWorld.sh, він виводить текст "Hello World" (і додає NewLine). Ніхто не буде сильно вражений цим результатом, проте ми будемо знати, що це не так просто, як це виглядає :)

Щасливого кодування!


Краще робити складні речі простими, ніж робити прості речі складними.
Wildcard

1

Це об'єктно-орієнтована оболонка, що базується на Python, але вона має синтаксний близький Golang: https://github.com/alexst07/shell-plus-plus

Наприклад, спробуйте ловити:

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

перевантаження класу та оператора:

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)

і ви можете використовувати команди, подібні 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

Тепер, з якими предметами ви маєте справу з оболонкою більшу частину часу? Це файли / каталоги, процеси та їх взаємодія. Отже, це має подобатися f1.editчи щось подібне currentFile=f1.c ; .edit ; .compile ; .run. Абоd1.search(filename='*.c' string='int \*') . Або p1.stop, p1.bg. Це моє розуміння оошель.


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


}

щойно спробував ввести поняття oo в баш на основі посилань 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

Вибачте за коротку відповідь, але ось що.

hipersayanx створив статтю Об'єктно-орієнтоване програмування в Bash . В основному він привіт вкрадена $FUNCNAME, function, compgen, іexport створити як можна ближче до ООП можна отримати в Баш.

Холодна частина - це вона добре працює, і для створення класу потрібно лише кілька ліній котлопластингу.

Основні необхідні деталі:

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)"
}

Використання:

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

$instanceName_method

Тепер я це використав сам у своєму проекті AuditOps, і hipersayanx має додаткові подробиці про те, як це насправді працює на його сайті. Попередження про несправність, хоча це дуже башизм, не буде працювати ні з чим старше bash 4.0 і може призвести до головного болю при налагодженні. Хоча особисто я хотів би бачити більшу частину покриття котла, переробленого як клас.

Завжди розумніше використовувати серйозну мову сценаріїв OOP, як perl, ruby ​​та python, коли це краще підходить для вашого проекту. Однак, на мій чесний варіант, варто витратити час і зусилля при підтримці модульних скриптів bash, щоб використовувати цей метод OOP в bash.


0

Я розробляю на GitHub функцію, яка працює так само, як HashMap Object , shell_map .

Для створення " екземплярів HashMap " ця функція здатна створювати копії себе під різними іменами. Кожна нова копія функції буде мати різну змінну $ FUNCNAME. $ FUNCNAME потім використовується для створення простору імен для кожного екземпляра Map.

Клавіші карти - це глобальні змінні у формі $ FUNCNAME_DATA_ $ KEY, де $ KEY - ключ, доданий до Мапи. Ці змінні є динамічними змінними .

Нижче я поставлю спрощену версію його, щоб ви могли використовувати як приклад.

#!/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
}

Використання:

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!"

Ви, здається, неправильно зрозуміли, як працює підміна команд. Він замінюється результатом команди, що міститься в задніх колах, а не замінюється статусом повернення . Іншими словами, ваш код не робить те, що ви думаєте, що це робить.
Wildcard

Ага. Ви праві. Мої вибачення. Однак використовувати це було б набагато зрозуміліше carp "Some error message"; return.
Wildcard

Або [ -z "$KEY" ] && { carp "some message"; return;} немає необхідності в передплаті. Але насправді це схоже на фактичного кандидата на if ... elif ... elif ... else ... fiконструкцію - що не часто є найкращим вибором, але, мабуть, є тут. :) (Або, можливо, випускний перемикач.)
Wildcard

@Wildcard Я відредагував відповідь. Зараз наша дискусія про коропа та задумки не має сенсу. видалимо цю розмову?
Бруно Неґрао Зіка

0

Plumbum - мова оболонки, схожа на Python. Він пакує оболонку, як синтаксис з Python, роблячи досвід, орієнтований на об'єкт.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.