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.
_fn_init() { . /dev/fd/4 ; } 4<<INIT
${1}() { $(shift ; printf %s\\n "$@")
} 4<<-REQ 5<<-\\RESET
: \${_if_unset?shell will ERR and print this to stderr}
: \${common_param="REQ/RESET added to all funcs"}
REQ
_fn_init $(printf "'%s' " "$@")
RESET
INIT
URUCHOM
Wzywam tutaj _fn_init
do zadeklarowania funkcji o nazwie fn
.
set -vx
_fn_init fn \
'echo "this would be command 1"' \
'echo "$common_param"'
#OUTPUT#
+ _fn_init fn 'echo "this would be command 1"' 'echo "$common_param"'
shift ; printf %s\\n "$@"
++ shift
++ printf '%s\n' 'echo "this would be command 1"' 'echo "$common_param"'
printf "'%s' " "$@"
++ printf ''\''%s'\'' ' fn 'echo "this would be command 1"' 'echo "$common_param"'
#ALL OF THE ABOVE OCCURS BEFORE _fn_init RUNS#
#FIRST AND ONLY COMMAND ACTUALLY IN FUNCTION BODY BELOW#
+ . /dev/fd/4
#fn AFTER _fn_init .dot SOURCES IT#
fn() { echo "this would be command 1"
echo "$common_param"
} 4<<-REQ 5<<-\RESET
: ${_if_unset?shell will ERR and print this to stderr}
: ${common_param="REQ/RESET added to all funcs"}
REQ
_fn_init 'fn' \
'echo "this would be command 1"' \
'echo "$common_param"'
RESET
WYMAGANY
Jeśli chcę wywołać tę funkcję, umrze, chyba że _if_unset
zostanie ustawiona zmienna środowiskowa .
fn
#OUTPUT#
+ fn
/dev/fd/4: line 1: _if_unset: shell will ERR and print this to stderr
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ć.
_if_unset=set fn
#OUTPUT#
+ _if_unset=set
+ fn
+ echo 'this would be command 1'
this would be command 1
+ echo 'REQ/RESET added to all funcs'
REQ/RESET added to all funcs
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.
set +vx
_fn_init 'fn' \
'echo "Hi! I am the first function."' \
'echo "$common_param"'
_fn_init 'fn2' \
'echo "This is another function."' \
'echo "$common_param"'
_if_unset=set ;
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.
unset common_param ; echo
fn ; echo
fn2 ; echo
#OUTPUT#
Hi! I am the first function.
REQ/RESET added to all funcs
This is another function.
REQ/RESET added to all funcs
A teraz z zakresu dzwoniącego:
echo $common_param
#OUTPUT#
REQ/RESET added to all funcs
Ale teraz chcę, aby było to coś zupełnie innego:
common_param="Our common parameter is now something else entirely."
fn ; echo
fn2 ; echo
#OUTPUT#
Hi! I am the first function.
Our common parameter is now something else entirely.
This is another function.
Our common parameter is now something else entirely.
A jeśli się rozbroję _if_unset
?
unset _if_unset ; echo
echo "fn:"
fn ; echo
echo "fn2:"
fn2 ; echo
#OUTPUT#
fn:
dash: 1: _if_unset: shell will ERR and print this to stderr
fn2:
dash: 1: _if_unset: shell will ERR and print this to stderr
RESETOWANIE
Jeśli chcesz w dowolnym momencie zresetować stan funkcji, możesz to łatwo zrobić. Musisz tylko zrobić (z poziomu funkcji):
. /dev/fd/5
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.