W Pythonie możemy dekorować funkcje kodem, który jest automatycznie stosowany i wykonywany względem funkcji.
Czy jest jakaś podobna funkcja w bash?
W skrypcie, nad którym aktualnie pracuję, mam pewne okno testowe, które testuje wymagane argumenty i kończy działanie, jeśli nie istnieją - i wyświetla niektóre komunikaty, jeśli określono flagę debugowania.
Niestety muszę ponownie wstawić ten kod do każdej funkcji i jeśli chcę go zmienić, będę musiał zmodyfikować każdą funkcję.
Czy istnieje sposób, aby usunąć ten kod z każdej funkcji i zastosować go do wszystkich funkcji, podobnie jak dekoratory w pythonie?
Odpowiedzi:
Byłoby to o wiele łatwiejsze dzięki
zsh
anonimowym funkcjom i specjalnej tablicy asocjacyjnej z kodami funkcji. Dziękibash
jednak można zrobić coś takiego:Co da wynik:
Nie można jednak wywołać dekorowania dwa razy, aby dwukrotnie ozdobić swoją funkcję.
Z
zsh
:źródło
typeset
konieczny? Czy nie zadeklarowałby tego inaczej?eval "_inner_$(typeset -f x)"
tworzy_inner_x
jako dokładną kopię oryginałux
(taki sam jakfunctions[_inner_x]=$functions[x]
wzsh
).return
.Już kilkakrotnie omawiałem sposoby i sposoby działania poniższych metod, więc nie zrobię tego ponownie. Osobiście moje ulubione na ten temat są tutaj i tutaj .
Jeśli nie jesteś zainteresowany czytaniem tego, ale nadal jesteś ciekawy, po prostu zrozum, że tutaj-dokumenty dołączone do danych wejściowych funkcji są oceniane pod kątem rozszerzenia powłoki przed uruchomieniem funkcji i że są generowane na nowo w stanie, w jakim były, gdy funkcja została zdefiniowana za każdym razem, gdy funkcja jest wywoływana.
OGŁOSIĆ
Potrzebujesz tylko funkcji, która deklaruje inne funkcje.
URUCHOM
Wzywam tutaj
_fn_init
do zadeklarowania funkcji o nazwiefn
.WYMAGANY
Jeśli chcę wywołać tę funkcję, umrze, chyba że
_if_unset
zostanie ustawiona zmienna środowiskowa .Zwróć uwagę na kolejność śladów powłoki - nie tylko
fn
błąd kończy się, gdy_if_unset
jest wywoływany, gdy jest rozbrojony, ale nigdy nie działa w pierwszej kolejności . Jest to najważniejszy czynnik, który należy zrozumieć podczas pracy z rozszerzeniami dokumentu tutaj - muszą one zawsze występować jako pierwsze, ponieważ w końcu są<<input
.Błąd wynika z
/dev/fd/4
tego, że powłoka nadrzędna ocenia dane wejściowe przed przekazaniem ich do funkcji. Jest to najprostszy i najbardziej wydajny sposób testowania wymaganego środowiska.Tak czy inaczej, awarię można łatwo naprawić.
ELASTYCZNE
Zmienna
common_param
jest przetwarzana na wartość domyślną na wejściu dla każdej funkcji zadeklarowanej przez_fn_init
. Ale ta wartość jest również zmienna na każdą inną, która będzie również honorowana przez każdą funkcję podobnie zadeklarowaną. Zostawię teraz ślady pocisków - nie wchodzimy tutaj na żadne niezbadane terytorium ani nic.Powyżej deklaruję dwie funkcje i ustawiam
_if_unset
. Teraz, przed wywołaniem którejkolwiek z funkcji, rozbrojęcommon_param
, abyś mógł zobaczyć, że sami ją ustawią, kiedy je wywołam.A teraz z zakresu dzwoniącego:
Ale teraz chcę, aby było to coś zupełnie innego:
A jeśli się rozbroję
_if_unset
?RESETOWANIE
Jeśli chcesz w dowolnym momencie zresetować stan funkcji, możesz to łatwo zrobić. Musisz tylko zrobić (z poziomu funkcji):
Zapisałem argumenty użyte do początkowej deklaracji funkcji w
5<<\RESET
deskryptorze pliku wejściowego. Więc.dot
zaopatrywaniu że w powłoce w każdej chwili będzie powtórzyć proces, które wyróżniają go na pierwszym miejscu. Wszystko jest dość łatwe, naprawdę i prawie w pełni przenośne, jeśli chcesz przeoczyć fakt, że POSIX tak naprawdę nie określa ścieżek węzłów urządzeń deskryptorów plików (które są niezbędne dla powłoki.dot
).Możesz łatwo rozwinąć to zachowanie i skonfigurować różne stany dla swojej funkcji.
WIĘCEJ?
Nawiasem mówiąc, to ledwo rysuje powierzchnię. Często używam tych technik, aby osadzać małe funkcje pomocnicze, które można zadeklarować w dowolnym momencie na wejściu funkcji głównej - na przykład
$@
w razie potrzeby dla dodatkowych tablic pozycjonujących . W rzeczywistości - jak sądzę, musi to być coś bardzo zbliżonego do tego, co robią pociski wyższego rzędu. Widać, że są bardzo łatwo programowo nazwane.Chciałbym również zadeklarować funkcję generatora, która akceptuje ograniczony typ parametru, a następnie definiuje funkcję palnika do jednorazowego użytku lub w inny sposób ograniczoną zakresem wzdłuż linii lambda - lub funkcji liniowej - która jest po prostu
unset -f
sama, gdy przez. Możesz przekazać funkcję powłoki.źródło
eval
?.dot
działa z plikami i strumieniami, więc nie napotykasz tego samego rodzaju problemów z listą argumentów, które w przeciwnym razie możesz mieć. Mimo to jest to prawdopodobnie kwestia preferencji. Z pewnością uważam, że jest czystszy - szczególnie gdy zaczynasz ewaluować eval - to koszmar z miejsca, w którym siedzę..dot
dopóki nie będziesz dobry i gotowy - ani nigdy. Umożliwia to nieco więcej swobody w testowaniu jego ocen. I zapewnia elastyczność stanu na wejściu - z którą można sobie poradzić na inne sposoby - ale z tej perspektywy jest o wiele mniej niebezpieczny niż jesteval
.Myślę, że jednym ze sposobów drukowania informacji o funkcji, kiedy ty
jest zmiana wbudowanego bash
return
i / lubexit
na początku każdego skryptu (lub w jakimś pliku, który za każdym razem pozyskujesz przed uruchomieniem programu). Więc piszeszJeśli to uruchomisz, otrzymasz:
W razie potrzeby można to łatwo zaktualizować za pomocą flagi debugowania, nieco tak:
W ten sposób instrukcja zostanie wykonana tylko wtedy, gdy ustawiona jest zmienna VERBOSE (przynajmniej tak używam verbose w moich skryptach). Z pewnością nie rozwiązuje problemu funkcji dekorowania, ale może wyświetlać komunikaty w przypadku, gdy funkcja zwraca stan niezerowy.
Podobnie możesz przedefiniować
exit
, zastępując wszystkie wystąpieniareturn
, jeśli chcesz wyjść ze skryptu.EDYCJA: Chciałem dodać tutaj sposób, w jaki używam do dekorowania funkcji w bash, jeśli mam ich wiele i zagnieżdżone. Kiedy piszę ten skrypt:
A dla danych wyjściowych mogę uzyskać:
Może być pomocny dla kogoś, kto ma funkcje i chce je debugować, aby zobaczyć, w którym wystąpił błąd funkcji. Opiera się na trzech funkcjach, które można opisać poniżej:
Starałem się umieścić jak najwięcej w komentarzach, ale tutaj jest też opis: Używam
_ ()
funkcji jako dekorator, jeden ja umieścić po deklaracji każdej funkcji:foo () { _
. Ta funkcja wypisuje nazwę funkcji z odpowiednim wcięciem, w zależności od głębokości funkcji w innej funkcji (jako wcięcie domyślne używam 4 liczby spacji). Zwykle drukuję to na szaro, aby oddzielić to od zwykłego wydruku. Jeśli funkcja ma być dekorowana argumentami lub bez niej, można zmodyfikować przedostatnią linię w funkcji dekoratora.Aby wydrukować coś wewnątrz funkcji, wprowadziłem
print ()
funkcję, która drukuje wszystko, co jest do niej przekazywane z odpowiednim wcięciem.Funkcja
set_indentation_for_print_function
robi dokładnie to, co oznacza, obliczając wcięcie z${FUNCNAME[@]}
tablicy.W ten sposób występują pewne wady, na przykład nie można przekazać opcji
print
polubieniaecho
, np.-n
Lub-e
, a także, jeśli funkcja zwraca 1, nie jest dekorowana. A także w przypadku argumentów przekazywanych naprint
więcej niż szerokość terminala, które zostaną zawinięte na ekranie, nie będzie widać wcięcia dla zawiniętej linii.Świetnym sposobem na użycie tych dekoratorów jest umieszczenie ich w osobnym pliku i w każdym nowym skrypcie, aby pobrać ten plik
source ~/script/hand_made_bash_functions.sh
.Myślę, że najlepszym sposobem na włączenie dekoratora funkcji w bash jest napisanie dekoratora w treści każdej funkcji. Myślę, że o wiele łatwiej jest pisać funkcje wewnątrz funkcji w bash, ponieważ ma opcję ustawienia wszystkich zmiennych globalnych, a nie w standardowych językach obiektowych. To sprawia, że umieszczasz etykiety wokół kodu w bash. Przynajmniej pomogło mi to w skryptach debugujących.
źródło
Może przykłady dekoratora w projekcie http://sourceforge.net/projects/oobash/ mogą ci pomóc (oobash / docs / Examples / decorator.sh).
źródło
Dla mnie jest to najprostszy sposób na wdrożenie wzoru dekoratora w bash.
źródło
"$@"
).Dużo (być może za dużo :) wykonuję metaprogramowanie w Bash i znalazłem dekoratorów nieocenionych przy ponownym wdrażaniu zachowań w locie. Moja biblioteka pamięci podręcznej bash używa dekoracji do przezroczystego zapamiętywania funkcji Bash przy minimalnej ceremonii:
Oczywiście
bc::cache
robi coś więcej niż tylko dekorowanie, ale bazowa dekoracja polega nabc::copy_function
skopiowaniu istniejącej funkcji do nowej nazwy, dzięki czemu oryginalną funkcję można zastąpić dekoratorem.Oto prosty przykład dekoratora, który
time
jest dekorowaną funkcją, przy użyciubc::copy_function
:Próbny:
źródło