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

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

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

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

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

13 Solutions collect form web for “Объектно-ориентированная оболочка для * nix”

Я могу представить три желательные функции в оболочке:

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

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

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

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


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

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

  • irb (interactive ruby) – эквивалент Ruby консоли Python.

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

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

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


Добавление: еще одна часть инструментария unix обрабатывает многие вещи как файлы:

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

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

Вам не нужно много кода bash для реализации классов или объектов в bash.

Скажем, 100 строк.

У Bash есть ассоциативные массивы, которые могут быть использованы для реализации простой системы объектов с наследованием, методами и свойствами.

Таким образом, вы могли бы определить класс следующим образом:

 class Queue N=10 add=q_add remove=q_remove 

Создание экземпляра этой очереди может быть выполнено следующим образом:

 class Q:Queue N=100 

или

 inst Q:Queue N=100 

Поскольку классы реализуются с помощью массива, класс и inst – действительно синонимы – вроде как в 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 

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

 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 

ksh93t + вводит некоторые концепции OO, сохраняя синтаксис оболочки bourne / posix: http://blog.fpmurphy.com/2010/05/ksh93-using-types-to-create-object-orientated-scripts.html

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

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

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

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

Есть Rush, который использует ruby ​​и Psh, основанный на perl.

Если кто-то хочет только основы объектно-ориентированного программирования (свойства и методы), чем действительно простая структура, это поможет.

Предположим, вы хотите отобразить текст «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). На этот результат никто не будет впечатлен, однако мы узнаем, что это не так просто, как кажется 🙂

Счастливое кодирование!

jq работает как такой объектно-ориентированный слой.

Это немного проще в использовании и настройке, имеет имена args и т. Д. https://github.com/uudruid74/bashTheObjects

Я обновляю свой ответ на примере, который следует за одним из основных примеров, приведенных для другого ответа, но с этим синтаксисом. Пример программы похож, но вам не нужно префикс всех переменных с именем класса (он знает это как показывает метод вида ), и я думаю, что синтаксис намного проще!

Во-первых, файл класса. Значения по умолчанию для переменных экземпляра являются необязательными и используются только в том случае, если вы не передадите эти значения в конструктор.

 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 вместе с предстоящей поддержкой контейнера / итератора.

Оператор import технически не требуется, но он заставляет загрузку класса в данной точке вместо ожидания первого нового , что может помочь инициализировать вещи в правильном порядке. Обратите внимание на легкость, с которой вы можете сразу установить несколько переменных экземпляра.

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

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

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

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

Извините за короткий ответ, но здесь идет.

hipersayanx создал статью « Объектно-ориентированное программирование» в Bash . В основном он приветствовал $FUNCNAME , function , compgen и export чтобы создать как можно ближе к OOP, который можно получить в bash.

Прохладная часть – это хорошо работает, и для создания класса требуется всего несколько линий котельной.

Необходимы основные части:

 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, чтобы использовать этот метод ООП в bash.

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

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

Ключами карты являются глобальные переменные, в виде $ FUNCNAME_DATA_ $ KEY, где ключ $ KEY – это ключ, добавленный к карте. Эти переменные являются динамическими переменными .

Bellow Я поставлю упрощенную версию, чтобы вы могли использовать ее в качестве примера.

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

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

  • Сценарий для удаления файлов, не соответствующих определенным именам файлов
  • Как удалить самый старый каталог в данном каталоге?
  • Сценарий оболочки для проверки состояния точки монтирования на сервере linux
  • Сравнение файлов и их свойств
  • Сценарий Bash: команда test -o
  • Добавить путь к $ PATH, если он еще не был в $ PATH
  • Как запустить сценарий при обновлении папки в Solaris 5.10?
  • Как переместить каталог, файл по файлу? (вместо «copy then remove»)
  • Передача имени файла скриптам с функцией автозаполнения
  • Слияние двух файлов с помощью скриптов
  • Петля с двумя переменными в сценарии bash
  • Как создать zip-каталогов -mtime -150
  • Linux и Unix - лучшая ОС в мире.