Obiektowa powłoka dla * nix


38

Przedmowa: Uwielbiam bash i nie mam zamiaru rozpoczynać żadnego sporu lub świętej wojny i mam nadzieję, że nie jest to niezwykle naiwne pytanie.

To pytanie jest nieco związane z tym postem na superużytkowniku, ale nie sądzę, żeby OP naprawdę wiedział, o co prosi. Używam bash na FreeBSD, Linux, OS X i cygwin w Windows. Niedawno miałem również duże doświadczenie z programem PowerShell w systemie Windows.

Czy istnieje powłoka dla * nix, która jest już dostępna lub działa, która jest kompatybilna z bash, ale dodaje warstwę miksowania obiektowego do skryptu? Jedyne, co wiem na ten temat, to konsola python, ale o ile wiem, nie zapewnia ona dostępu do standardowego środowiska powłoki. Na przykład, nie mogę po prostu cd ~i ls, następnie chmod +x filewewnątrz konsoli Pythona. Musiałbym używać Pythona do wykonywania tych zadań zamiast standardowych plików binarnych unix lub wywoływać pliki binarne za pomocą kodu python.

Czy taka skorupa istnieje?


3
Jest Pash, ale to znacznie bardziej podobny do Powershell niż Bash.
ephemient

1
@ efhemient może powinieneś napisać odpowiedź na pash ... chociaż nic o tym nie wiem, iirc, PowerShell jest powłoką OO.
ksenoterrakid,

4
Hej, powinieneś sprawdzić ipython . Jeśli wpiszesz wyrażenie, które nie ma sensu jako python, spróbuje odwzorować je na polecenie powłoki. Na przykład rzeczy takie jak cd ~po tym lsdziała jak w Bash. Możesz także przypisać dane wyjściowe zmiennym Pythona (listy linii ... w pewnym sensie) za pomocą poleceń takich jak listing = !ls.
intuicyjnie

@intuited: super, sprawdzę to
Robert S Ciaccio

1
@intuited: iPython był całkiem dobry do rzeczy, które chcę robić, dzięki!
Robert S Ciaccio

Odpowiedzi:


43

Mogę wymyślić trzy pożądane cechy powłoki:

  • Interaktywna użyteczność: typowe polecenia powinny być szybkie do wpisania; ukończenie; ...
  • Programowanie: struktury danych; współbieżność (zadania, potok, ...); ...
  • Dostęp do systemu: praca z plikami, procesami, oknami, bazami danych, konfiguracją systemu, ...

Powłoki uniksowe zwykle koncentrują się na aspekcie interaktywnym i podzlecają większość dostępu do systemu i część programowania zewnętrznym narzędziom, takim jak:

  • BC dla prostej matematyki
  • openssl do kryptografii
  • sed , awk i inne do przetwarzania tekstu
  • nc dla podstawowej sieci TCP / IP
  • ftp dla FTP
  • mail, Mail, mailx, Itd. Dla podstawowych e-mail
  • cron dla zaplanowanych zadań
  • wmctrl do podstawowej manipulacji oknem X.
  • dcop dla bibliotek KDE ≤3.x.
  • narzędzia dbus ( dbus-*lub qdbus ) do różnych informacji systemowych i zadań konfiguracyjnych (w tym nowoczesnych środowisk pulpitu, takich jak KDE ≥4)

Wiele, wiele rzeczy można zrobić, wywołując polecenie z właściwymi argumentami lub danymi wejściowymi. Jest to bardzo skuteczne podejście - lepiej mieć jedno narzędzie na zadanie, które robi to dobrze, niż jeden program, który robi wszystko, ale źle - ale ma swoje ograniczenia.

Głównym ograniczeniem powłok unixowych i podejrzewam, że tego właśnie wymaga twój „skryptowy obiektowy wymóg”, ponieważ nie są dobre w zatrzymywaniu informacji z jednego polecenia do drugiego lub łączeniu poleceń w sposób bardziej wyszukany niż rurociąg. W szczególności komunikacja między programami jest oparta na tekście, więc aplikacje można łączyć tylko wtedy, gdy serializują one swoje dane w zgodny sposób. Jest to zarówno błogosławieństwo, jak i przekleństwo: podejście „wszystko jest tekstem” ułatwia szybkie wykonywanie prostych zadań, ale podnosi barierę dla bardziej złożonych zadań.

Interaktywna użyteczność działa również w przeciwieństwie do obsługi programu. Programy interaktywne powinny być krótkie, wymagać niewielkiego cytowania, nie zawracać sobie głowy zmiennymi deklaracjami lub pisaniem itp. Programy, które można utrzymywać, powinny być czytelne (więc nie mogą mieć wielu skrótów), powinny być czytelne (więc nie musisz się zastanawiać, czy jedno słowo jest łańcuchem, nazwą funkcji, nazwą zmiennej itp.), powinien mieć sprawdzane spójność, takie jak deklaracje zmiennych i pisanie itp.

Podsumowując, powłoka jest trudnym do osiągnięcia kompromisem. Ok, to kończy sekcję rant na przykładach.


  • Perl Shell (PSH) „łączy interaktywny charakter Unix shell z mocą Perl”. Proste polecenia (nawet potoki) można wprowadzać w składni powłoki; wszystko inne to Perl. Projekt nie był długo rozwijany. Jest użyteczny, ale nie osiągnął punktu, w którym rozważałbym użycie go nad czystym Perlem (do skryptów) lub czystą powłoką (interaktywnie lub do skryptów).

  • IPython to ulepszona interaktywna konsola Python, szczególnie ukierunkowana na obliczenia numeryczne i równoległe. To stosunkowo młody projekt.

  • irb (interaktywny ruby) to Rubinowy odpowiednik konsoli Pythona.

  • scsh to implementacja schematu (tzn. przyzwoity język programowania) z rodzajem powiązań systemowych tradycyjnie występujących w powłokach uniksowych (ciągi, procesy, pliki). Jednak nie ma na celu być użytecznym jako interaktywna powłoka.

  • zsh to ulepszona interaktywna powłoka. Jego mocną stroną jest interaktywność (edycja wiersza poleceń, uzupełnianie, typowe zadania realizowane zwięzłą, ale tajemniczą składnią). Jego funkcje programowania nie są świetne (na równi z ksh), ale zawiera wiele bibliotek do kontroli terminali, wyrażeń regularnych, sieci itp.

  • fish to czysty start w powłoce uniksowej. Nie ma lepszych funkcji programowania ani dostępu do systemu. Ponieważ łamie kompatybilność z sh, ma więcej miejsca na rozwijanie lepszych funkcji, ale tak się nie stało.


Dodatek: kolejna część uniksowego zestawu narzędzi traktuje wiele rzeczy jako pliki:

  • Większość urządzeń jest dostępna w postaci plików.
  • Pod Linuksem /syszapewnia większą kontrolę nad sprzętem i systemem.
  • W wielu wariantach Uniksa kontrola procesu może odbywać się za pośrednictwem /procsystemu plików.
  • FUSE ułatwia pisanie nowych systemów plików. Istnieją już systemy plików do konwertowania formatów plików w locie, uzyskiwania dostępu do plików za pośrednictwem różnych protokołów sieciowych, przeszukiwania archiwów itp.

Być może przyszłością powłok unixowych nie jest lepszy dostęp do systemu za pomocą poleceń (i lepsze struktury kontrolne do łączenia poleceń), ale lepszy dostęp do systemu za pośrednictwem systemów plików (które łączą się nieco inaczej - nie sądzę, że opracowaliśmy kluczowe idiomy (takie jak rura płaszczowa) są jeszcze).


1
Uderzyłeś się w głowę zaraz po tym, jak powiedziałeś „Podejrzewam, że o to ci chodzi”. Głównym powodem, dla którego zadaję to pytanie, jest to, że uwielbiam mieć moc narzędzi uniksowych, ale interakcja tekstowa między programami zdecydowanie „podnosi barierę dla bardziej złożonych zadań”. Spędziłem wystarczająco dużo dni programistycznych na pisaniu parserów tekstowych :) Myślę, że to bardzo dobrze przemyślana odpowiedź. Dochodzi do sedna problemu i złożoności tematu. Chciałbym móc dwukrotnie go głosować: P
Robert S. Ciaccio,

1
+1 za ipython, chociaż nie mam pojęcia, co chce zrobić OP.
Falmarri,

1
To świetna odpowiedź: szczerze sądzę, że ziarna interesującej pracy doktorskiej są tutaj.
Ziggy,

1
@RobertSCiaccio Ta odpowiedź została właśnie połączona w ostatnim poście, a twój komentarz na temat analizowania tekstu zmusił mnie do myślenia ... jeśli parsowanie tekstu wystarczy do wykonania „złożonych zadań”, to czy nie możesz mieć małego skryptu lub programu, który implementuje używać go jako funkcji w skryptach bash? To tylko myśl, że nie mam za dużo doświadczenia w tworzeniu skryptów bash.
Oxwivi

1
@onlyanegg W jaki sposób można powiedzieć, że ryba jest „obiektowa”? Ryba ma przede wszystkim być prostsza. Czy jest jakiś sposób, aby był silniejszy niż alternatywy?
Gilles „SO- przestań być zły”

13

Nie potrzebujesz dużo kodu bash, aby zaimplementować klasy lub obiekty w bash.

Powiedz 100 linii.

Bash ma tablice asocjacyjne, których można użyć do wdrożenia prostego systemu obiektowego z dziedziczeniem, metodami i właściwościami.

Więc możesz zdefiniować taką klasę:

class Queue N=10 add=q_add remove=q_remove

Utworzenie wystąpienia tej kolejki można wykonać w następujący sposób:

class Q:Queue N=100

lub

inst Q:Queue N=100

Ponieważ klasy są implementowane za pomocą tablicy, klasa i inst są naprawdę synonimami - podobnie jak w javascript.

Dodawanie elementów do tej kolejki można wykonać w następujący sposób:

$Q add 1 2 aaa bbb "a string"

Usuwanie elementów do zmiennej X można wykonać w następujący sposób:

$Q remove X

Strukturę zrzutu obiektu można wykonać w następujący sposób:

$Q dump

Co zwróciłoby coś takiego:

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

Klasy są tworzone przy użyciu funkcji klasy takiej jak ta:

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

UWAGA: Podczas definiowania nowej klasy lub instancji można zastąpić dowolną wartość elementu lub funkcję.

Tablice asocjacyjne Bash mają dziwaczność, która sprawia, że ​​działa to dobrze: $ Q [0]} jest identyczny z $ Q. Oznacza to, że możemy użyć nazwy tablicy do wywołania funkcji wysyłki metody:

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

Minusem jest to, że nie mogę użyć [0] dla danych, więc moje kolejki (w tym przypadku) zaczynają się od indeksu = 1. Alternatywnie mogłem użyć wskaźników asocjacyjnych, takich jak „q + 0”.

Aby uzyskać i ustawić członków, możesz zrobić coś takiego:

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

Aby zrzucić strukturę obiektu, zrobiłem to:

UWAGA: Nie jest to wymagane dla OOP w bash, ale dobrze jest zobaczyć, jak powstają obiekty.

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

Mój projekt OOP nie uwzględniał obiektów w obiektach - z wyjątkiem dziedziczonej klasy. Możesz utworzyć je osobno lub stworzyć specjalny konstruktor, taki jak class (). * obj_dump * musiałby zostać zmodyfikowany w celu wykrycia klas wewnętrznych w celu ich rekurencyjnego wydrukowania.

O! i ręcznie definiuję klasę ROOT, aby uprościć funkcję klasy :

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

Za pomocą kilku funkcji kolejek zdefiniowałem kilka takich klas:

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

Utworzono niektóre instancje kolejki i uruchomiono je:

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

Może działa, ale jest brzydki . I zupełnie nie po to bash. Przypomina mi odpowiedź Stephane'a na temat tego, dlaczego nie używać pętli powłoki do przetwarzania tekstu, szczególnie sekcja zatytułowana „koncepcyjnie”, która szczegółowo opisuje różnicę w celach między językami takimi jak C i bash. unix.stackexchange.com/a/169765/135943
Wildcard

1
Może działać? To działa, ale chodzi o to, że chociaż nie jest źle, po prostu się nie robi. Nie odpowiedziałem również na pytanie OP, ale jeśli bash jest TC, to pomyślałem, że powinno być w stanie przetwarzać obiekty. I wielu to udowodniło.
philcolbourn


5

IPython jest zaskakująco wygodny w użyciu.

Standardowe funkcje powłoki: kontrola zadań, edycja i historia readline, aliasy cat ls cdi pwdintegracja z pageriem, uruchamianie dowolnej komendy systemowej przez poprzedzenie jej !lub włączenie %rehashx, dane wyjściowe komendy przypisywane do zmiennej python, wartości python dostępne jako zmienne powłoki.

Specyficzne dla Pythona: ponowne wykorzystanie wyników z ostatnich poleceń, szybki dostęp do dokumentacji i źródła, przeładowanie modułu, debugger. Niektóre wsparcie klastra, jeśli jesteś w to zaangażowany.

To powiedziawszy, uruchamianie złożonych potoków nie odbywa się w Pythonie; będziesz również używać powłoki posix, tylko z klejem, aby przekazywać wartości tam i z powrotem.




2

Ten jest nieco prostszy w użyciu i konfiguracji, nazwał args itp. Https://github.com/uudruid74/bashTheObjects

Aktualizuję swoją odpowiedź za pomocą przykładu, który jest zgodny z jednym z podstawowych przykładów podanych dla innej odpowiedzi, ale z tą składnią. Przykładowy program jest podobny, ale nie musisz poprzedzać wszystkich zmiennych nazwą klasy (zna to, jak pokazuje metoda kindof ) i myślę, że składnia jest znacznie prostsza!

Najpierw plik klasy. Domyślne zmienne instancji są opcjonalne i są używane tylko wtedy, gdy nie przekazujesz tych wartości do konstruktora.

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
}

Teraz na przykład użycie:

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

UWAGI:

  1. To ostatnie twierdzenie się nie powiedzie. W przeciwieństwie do powyższego przykładu biblioteka nie obsługuje jeszcze przypisywania obiektów, ale nie byłoby to zbyt trudne do dodania. Umieszczę go na moim TO-DO wraz z nadchodzącą obsługą kontenera / iteratora.

Instrukcja importu nie jest technicznie wymagana, ale wymusza ładowanie klasy w danym punkcie zamiast oczekiwania na pierwszą nową , co może pomóc w zainicjowaniu rzeczy we właściwej kolejności. Zwróć uwagę na łatwość, z jaką możesz ustawić wiele zmiennych instancji jednocześnie.

Istnieją również poziomy debugowania, konstruktory, destruktory, podklasy i podstawowy system odbicia , a pokazane jest print / println w celu zastąpienia echa (czy kiedykolwiek próbowałeś wydrukować zmienną rozpoczynającą się od myślnika?). Przykład na github pokazuje, że działa jako CGI generujący HTML z klas.

Sama biblioteka (oop.lib.sh) nie jest taka prosta (ponad 400 wierszy, 11 KB), ale wystarczy ją dołączyć i zapomnieć.



1

Jeśli ktoś chce tylko podstaw programowania obiektowego (właściwości i metody), załatwi to naprawdę prosty framework.

Powiedzmy, że chcesz wyświetlać tekst „Hello World” za pomocą obiektów. Najpierw tworzysz klasę obiektową, która ma właściwość wyświetlanego tekstu oraz kilka metod ustawiania i wyświetlania tego tekstu. Aby pokazać, jak wiele instancji klasy może ze sobą współpracować, dodałem dwie metody wyświetlania tekstu: jedną z NewLine na końcu i jedną bez tego.

Plik definicji klasy: 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"
}

Zwróć uwagę na słowo „<<InstanceName>>”. Zostanie to później zastąpione, aby utworzyć wiele instancji obiektu klasy. Przed użyciem instancji obiektu potrzebujesz funkcji, która go faktycznie tworzy. Dla uproszczenia będzie to osobny skrypt o nazwie: 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"
}

Mamy teraz plik definicji klasy i funkcję CreateObject, która tworzy kopię tego pliku z tekstem „<<InstanceName>>” pod dowolną nazwą.

Użyjmy naszego nowego obiektu w skrypcie o nazwie: HelloWorld.sh (pamiętaj, że HelloWorld.sh powinien być wykonywalny. Pozostałe dwa pliki nie muszą)

# 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

Uruchamiając skrypt HelloWorld.sh wyświetla tekst „Hello World” (i dodaje NewLine). Nikt nie będzie pod wrażeniem tego wyniku, ale będziemy wiedzieć, że nie jest to takie proste, jak się wydaje :)

Miłego kodowania!


Lepiej jest uprościć skomplikowane rzeczy niż skomplikować proste.
Wildcard,

1

Jest to obiektowa powłoka oparta na Pythonie, ale ma sintaxe zbliżone do Golanga: https://github.com/alexst07/shell-plus-plus

Na przykład spróbuj złapać:

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

przeciążenie klasy i operatora:

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)

i możesz użyć poleceń podobnego 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

A teraz, z jakimi przedmiotami przez większość czasu masz do czynienia z powłoką? To pliki / katalogi, procesy i ich interakcja. Tak powinno być f1.editlub coś w tym stylu currentFile=f1.c ; .edit ; .compile ; .run. Lub d1.search(filename='*.c' string='int \*'). Lub p1.stop, p1.bg. Takie jest moje rozumienie 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


}

właśnie próbowałem wprowadzić koncepcje oo do bash na podstawie referencji 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

Przepraszam za krótką odpowiedź, ale proszę bardzo.

hipersayanx napisał artykuł Programowanie obiektowe w Bash . Zasadniczo on hi-okradł $FUNCNAME, function, compgen, i exportstworzyć jak najbliżej OOP można dostać w bash.

Fajne jest to, że działa dobrze i do zbudowania klasy wystarczy kilka linii kotłowania.

Podstawowe potrzebne części to:

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

Stosowanie:

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

$instanceName_method

Teraz wykorzystałem to sam w moim projekcie AuditOps, a hipersayanx ma dalsze szczegóły na temat tego, jak to naprawdę działa na jego stronie. Ostrzeżenie o taryfie, choć jest to bardzo bashism, nie będzie działać z niczym starszym niż bash 4.0 i może prowadzić do bólu głowy podczas debugowania. Chociaż osobiście chciałbym, aby większość poszycia bojlera została przerobiona jako sama klasa.

Zawsze lepiej jest używać poważnego języka skryptowego OOP, takiego jak perl, ruby ​​i python, gdy lepiej pasuje do twojego projektu. Jednak w mojej uczciwej opcji warto poświęcić czas i wysiłek podczas utrzymywania modułowych skryptów bash, aby korzystać z tej metody OOP w bash.


0

W GitHub rozwijam funkcję, która działa podobnie jak obiekt HashMap , shell_map .

Aby utworzyć „ instancje HashMap ”, ta funkcja może tworzyć własne kopie pod różnymi nazwami. Każda nowa kopia funkcji będzie miała inną zmienną $ FUNCNAME. $ FUNCNAME jest następnie używany do utworzenia przestrzeni nazw dla każdej instancji Map.

Klucze mapy to zmienne globalne w postaci $ FUNCNAME_DATA_ $ KEY, gdzie $ KEY to klucz dodany do mapy. Te zmienne są zmiennymi dynamicznymi .

Poniżej przedstawię jego uproszczoną wersję, abyś mógł ją wykorzystać jako przykład.

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

Stosowanie:

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

Wygląda na to, że źle zrozumiałeś, jak działa zastępowanie poleceń. Jest on zastępowany danymi wyjściowymi polecenia zawartymi w backticks, a nie zastępowany statusem return . Innymi słowy, twój kod nie robi tego, co myślisz.
Wildcard,

Aha. Masz rację. Przepraszam. Jednak użycie carp "Some error message"; returntego byłoby znacznie bardziej przejrzyste .
Wildcard

Lub [ -z "$KEY" ] && { carp "some message"; return;} Nie potrzebujesz podpowłoki. Ale tak naprawdę wygląda to na faktycznego kandydata na if ... elif ... elif ... else ... fikonstrukt - co często nie jest najlepszym wyborem, ale prawdopodobnie jest tutaj. :) (A może zmiana przypadku.)
Wildcard,

@Wildcard Zredagowałem odpowiedź. Teraz nasza dyskusja na temat karpi i backticksów nie ma sensu. usuńmy tę rozmowę?
Bruno Negrão Zica,

0

Plumbum to język powłoki podobny do Pythona. Pakuje powłokę jak składnia z Pythonem, co czyni obiekt zorientowanym obiektowo.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.