Ho già discusso di come e come il modo in cui i metodi seguenti funzionano in diverse occasioni prima, quindi non lo farò più. Personalmente, i miei preferiti sull'argomento sono qui e qui .
Se non sei interessato a leggerlo, ma sei ancora curioso, basta capire che i documenti qui allegati all'input della funzione vengono valutati per l'espansione della shell prima dell'esecuzione della funzione e che vengono generati di nuovo nello stato in cui si trovavano quando la funzione è stata definita ogni volta che viene chiamata la funzione.
DICHIARARE
Hai solo bisogno di una funzione che dichiari altre funzioni.
_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
ESEGUIRLO
Qui invito _fn_init
a dichiararmi una funzione chiamata 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
NECESSARIO
Se voglio chiamare questa funzione, morirà a meno che non _if_unset
sia impostata la variabile di ambiente .
fn
#OUTPUT#
+ fn
/dev/fd/4: line 1: _if_unset: shell will ERR and print this to stderr
Notare l'ordine delle tracce della shell: non solo fn
fallisce quando viene chiamato quando _if_unset
non è impostato, ma non viene mai eseguito in primo luogo . Questo è il fattore più importante da capire quando si lavora con espansioni di documenti qui: devono sempre verificarsi prima perché, <<input
dopo tutto, lo sono .
L'errore deriva dal /dev/fd/4
fatto che la shell padre sta valutando quell'input prima di consegnarlo alla funzione. È il modo più semplice ed efficiente per testare l'ambiente richiesto.
Ad ogni modo, l'errore è facilmente risolto.
_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
FLESSIBILE
La variabile common_param
viene valutata su un valore predefinito in input per ogni funzione dichiarata da _fn_init
. Ma quel valore è anche mutevole per ogni altro che sarà onorato anche da ogni funzione dichiarata in modo simile. Lascerò le tracce della conchiglia ora - non entreremo in nessun territorio inesplorato qui o altro.
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 ;
Sopra dichiaro due funzioni e ho impostato _if_unset
. Ora, prima di chiamare una delle due funzioni, deselezionerò in common_param
modo da poter vedere che si configureranno automaticamente quando le chiamo.
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
E ora dall'ambito del chiamante:
echo $common_param
#OUTPUT#
REQ/RESET added to all funcs
Ma ora voglio che sia completamente qualcos'altro:
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.
E se mi disinserissi _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
RIPRISTINA
Se è necessario ripristinare lo stato della funzione in qualsiasi momento, è possibile farlo facilmente. Devi solo fare (dall'interno della funzione):
. /dev/fd/5
Ho salvato gli argomenti utilizzati per dichiarare inizialmente la funzione nel 5<<\RESET
descrittore di file di input. In modo .dot
tale che nella shell in qualsiasi momento ripeterà il processo che lo ha impostato in primo luogo. È tutto abbastanza semplice, davvero e praticamente completamente portatile se si è disposti a trascurare il fatto che POSIX in realtà non specifica i percorsi dei nodi del dispositivo descrittore di file (che sono una necessità per la shell .dot
).
È possibile espandere facilmente questo comportamento e configurare stati diversi per la propria funzione.
DI PIÙ?
Questo a malapena graffia la superficie. Uso spesso queste tecniche per incorporare in qualsiasi momento piccole funzioni di supporto dichiarabili nell'input di una funzione principale, ad esempio per ulteriori $@
array posizionali, se necessario. In effetti - come credo, dev'essere qualcosa di molto vicino a ciò che fanno comunque i gusci di ordine superiore. Puoi vedere che sono nominati in modo molto programmatico.
Mi piace anche dichiarare una funzione del generatore che accetta un tipo limitato di parametro e quindi definisce una funzione del bruciatore monouso o comunque limitata dall'ambito lungo le linee di una lambda - o una funzione in linea - che è semplicemente unset -f
se stessa quando attraverso. È possibile passare una funzione shell in giro.