Объектно-ориентированная оболочка для * nix

38

Предисловие: я люблю bash и не собираюсь начинать какие-либо споры или священные войны, и, надеюсь, это не наивный вопрос.

Этот вопрос несколько связан с этим постом о суперпользователе, но я не думаю, что ОП действительно знал, о чем он просил. Я использую bash на FreeBSD, Linux, OS X и Cygwin на Windows. У меня также был большой опыт работы с PowerShell в Windows.

Существует ли оболочка для * nix, уже доступная или находящаяся в разработке, которая совместима с bash, но добавляет в смесь слой объектно-ориентированных сценариев? Единственное, что я знаю об этом, это консоль python, но, насколько я могу судить, она не обеспечивает доступ к стандартной среде оболочки. Например, я не могу просто cd ~и lsтогда chmod +x fileвнутри консоли python. Я должен был бы использовать Python для выполнения этих задач, а не стандартные двоичные файлы Unix, или вызывать двоичные файлы с использованием кода Python.

Существует ли такая оболочка?

Роберт С Чаччо
источник
3
Есть Pash, но он гораздо больше похож на Powershell, чем на Bash.
2010 года
1
@ephemient может, тебе стоит написать ответ для pash ... хотя я ничего не знаю об этом, iirc, powershell - это оболочка OO.
ксенотеррацид
4
Эй, ты должен проверить ipython . Если вы введете выражение, которое не имеет смысла как python, оно попытается сопоставить его с командой оболочки. Например, такие вещи, как cd ~следуют lsработы, как в Bash. Вы также можете назначить вывод переменным Python (списки строк ... вроде) с помощью команд вроде listing = !ls.
интуитивно
@intuited: круто, я проверю
Роберт С. Чаччо
1
@intuited: iPython очень хорош для того, что я хочу сделать, спасибо!
Роберт С Чаччо

Ответы:

43

Я могу думать о трех желательных особенностях в оболочке:

  • Интерактивное удобство использования: общие команды должны быстро набираться; завершение; ...
  • Программирование: структуры данных; параллелизм (работа, труба, ...); ...
  • Доступ к системе: работа с файлами, процессами, окнами, базами данных, настройка системы, ...

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

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

Много-много чего можно сделать, вызвав команду с правильными аргументами или по конвейеру. Это очень мощный подход - лучше иметь один инструмент на задачу, который делает это хорошо, чем одну программу, которая делает все, но плохо - но у нее есть свои ограничения.

Основное ограничение оболочек Unix, и я подозреваю, что это то, что вам нужно с вашим требованием «объектно-ориентированных сценариев», заключается в том, что они не способны сохранять информацию от одной команды к другой или комбинировать команды более изящными способами, чем трубопровод. В частности, межпрограммное взаимодействие основано на тексте, поэтому приложения можно объединять, только если они сериализуют свои данные совместимым способом. Это и благословение, и проклятие: подход «все в тексте» облегчает быстрое выполнение простых задач, но создает барьер для более сложных задач.

Интерактивное удобство использования также противоречит удобству сопровождения программы. Интерактивные программы должны быть короткими, требовать небольшого количества кавычек, не беспокоить вас объявлениями переменных или вводить текст и т. Д. Поддерживаемые программы должны быть читаемыми (поэтому не иметь много сокращений), должны быть читаемыми (чтобы вам не приходилось задумываться, является ли слово голым) является строкой, именем функции, именем переменной и т. д.), должны иметь проверки непротиворечивости, такие как объявления переменных и типирование и т. д.

Таким образом, оболочка является сложным компромиссом. Хорошо, на этом заканчивается раздел rant, на примерах.


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

  • IPython - это улучшенная интерактивная консоль Python, особенно ориентированная на численные и параллельные вычисления. Это относительно молодой проект.

  • irb (интерактивный ruby) - это Ruby-эквивалент консоли Python.

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

  • Zsh - улучшенная интерактивная оболочка. Его сильной стороной является интерактивность (редактирование командной строки, завершение, общие задачи, выполненные с кратким, но загадочным синтаксисом). Его функции программирования не так уж хороши (наравне с ksh), но он поставляется с рядом библиотек для управления терминалами, регулярных выражений, работы в сети и т. Д.

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


Приложение: другая часть инструментария Unix рассматривает многие вещи как файлы:

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

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

Жиль "ТАК - прекрати быть злым"
источник
1
Вы ударяете ноготь по голове сразу после того, как говорите: «Я подозреваю, что это то, что вам нужно». Основная причина, по которой я задаю этот вопрос, заключается в том, что мне нравится использовать инструменты Unix, но текстовое взаимодействие между программами определенно «создает барьер для более сложных задач». Я потратил достаточно времени на программирование для написания текстовых парсеров :) Я думаю, что это очень хорошо продуманный ответ. Это касается сути проблемы и сложности предмета. Хотелось бы, чтобы я проголосовал дважды: P
Роберт С. Чаччо
1
+1 для ipython, хотя я понятия не имею, что хочет сделать ОП.
Фальмарри
1
Это отличный ответ: я, честно говоря, думаю, что семена интересной кандидатской диссертации здесь.
Зигги
1
@RobertSCiaccio Этот ответ был только что связан с недавним сообщением, и ваш комментарий о разборе текста заставил меня задуматься ... если синтаксический анализ текста достаточен для выполнения ваших "сложных задач", то вы не могли бы иметь небольшой сценарий или программу, которая реализует и использовать это как какую-то функцию в ваших скриптах bash? Просто мысль, у меня нет большого опыта написания сценариев bash, о котором я могу говорить.
Oxwivi
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] для данных, поэтому мои очереди (в данном случае) начинаются с index = 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
}

И свалить структуру объекта, я сделал это:

ПРИМЕЧАНИЕ. Это не требуется для ООП в 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
}

Мой дизайн ООП не рассматривал объекты внутри объектов - за исключением унаследованного класса. Вы можете создать их отдельно или создать специальный конструктор, такой как 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

Создал несколько экземпляров Queue и заставил их работать:

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
philcolbourn
источник
Может работать, но это некрасиво . И совсем не то bash, для чего. Напоминает мне ответ Стефана о том, почему бы не использовать циклы оболочки для обработки текста, особенно раздел, озаглавленный «концептуально», который детализирует различие в целях между языками, такими как C и bash. unix.stackexchange.com/a/169765/135943
подстановочный
1
Может работать? Это работает, но ваша точка зрения в том, что, хотя и не так, это просто не сделано. Я также не ответил на вопрос OP, но если bash - TC, то я подумал, что он сможет обрабатывать объекты. И многие это продемонстрировали.
Philcolbourn
5

IPython удивительно удобен в использовании.

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

Специфично для Python: повторное использование результатов последних команд, быстрый доступ к документации и исходным кодам, перезагрузка модуля, отладчик. Некоторая поддержка кластера, если вы в этом.

Тем не менее, запуск сложных каналов не выполняется в Python; вы также будете использовать оболочку posix, просто с небольшим количеством клея для передачи значений туда и сюда.

Tobu
источник
2

jq работает довольно хорошо, как такой вид объектно-ориентированного слоя.

Abbafei
источник
2

Этот немного проще в использовании и настройке, имеет именованные аргументы и т. Д. 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), но вы просто включаете ее и забываете.

Эван Ланглуа
источник
2

Теперь вы можете установить PowerShell Core Edition на Linux. Он работает на кроссплатформенной платформе .NET Core, которая активно разрабатывается Microsoft.

Тревор Салливан
источник
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"
}

Обратите внимание на слово «<<имя-экземпляра>>». Это будет заменено позже, чтобы создать несколько экземпляров объекта класса. Прежде чем вы сможете использовать экземпляр объекта, вам нужна функция, которая фактически его создает. Для простоты это будет отдельный скрипт с именем: 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). Никто не будет впечатлен этим результатом, однако мы будем знать, что это не так просто, как кажется :)

Удачного кодирования!

vandor76
источник
Лучше делать сложные вещи простыми, чем делать простые вещи сложными.
Wildcard
1

Это объектно-ориентированная оболочка, основанная на Python, но с синтаксическим закрытием Golang: https://github.com/alexst07/shell-plus-plus

Например, попробуйте catch:

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
Alex
источник
0

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

ott--
источник
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


}

только что попробовал ввести ооо понятия в bash на основе ссылки 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 есть дополнительная информация о том, как это на самом деле работает на его сайте. Предупреждение о тарифе, хотя это очень bashism, не будет работать с чем-то более старым, чем bash 4.0 и может привести к головной боли при отладке. Хотя лично мне бы хотелось, чтобы большая часть обшивки котла переделывалась как сама класс.

Всегда лучше использовать серьезный язык сценариев ООП, такой как perl, ruby ​​и python, когда он лучше подходит для вашего проекта. Однако, по моему честному варианту, при поддержке модульных сценариев bash стоит потратить время и силы на использование этого метода ООП в bash.

Дуайт Спенсер
источник
0

Я разрабатываю на GitHub функцию, которая работает так же, как HashMap Object , shell_map .

Для того, чтобы создать экземпляров HashMap » эта функция может создавать свои копии под разными именами. Каждая новая копия функции будет иметь свою переменную $ FUNCNAME. Затем $ FUNCNAME используется для создания пространства имен для каждого экземпляра карты.

Ключи карты являются глобальными переменными в форме $ 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, делая опыт объектно-ориентированным.

joshlk
источник