Eu já discuti como e por que os métodos abaixo funcionam em várias ocasiões antes, então não o farei novamente. Pessoalmente, meus próprios favoritos no tópico estão aqui e aqui .
Se você não estiver interessado em ler isso, mas ainda assim curioso, entenda que os documentos aqui anexados à entrada da função são avaliados quanto à expansão do shell antes da execução da função e que eles são gerados novamente no estado em que estavam quando a função foi definida toda vez que a função é chamada.
DECLARAR
Você só precisa de uma função que declare outras funções.
_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
EXECUTÁ-LO
Aqui apelo _fn_init
para me declarar uma função chamada 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
REQUERIDOS
Se eu quiser chamar essa função, ela morrerá, a menos que a variável de ambiente _if_unset
esteja definida.
fn
#OUTPUT#
+ fn
/dev/fd/4: line 1: _if_unset: shell will ERR and print this to stderr
Observe a ordem dos rastreamentos do shell - não apenas fn
falha quando chamada quando _if_unset
está desconfigurada, mas nunca é executada em primeiro lugar . Esse é o fator mais importante a ser entendido ao trabalhar com expansões de documentos aqui - elas sempre devem ocorrer primeiro porque são <<input
afinal.
O erro ocorre /dev/fd/4
porque o shell pai está avaliando essa entrada antes de entregá-la à função. É a maneira mais simples e eficiente de testar o ambiente necessário.
De qualquer forma, a falha é facilmente remediada.
_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
FLEXÍVEL
A variável common_param
é avaliada como um valor padrão na entrada para cada função declarada por _fn_init
. Mas esse valor também pode ser alterado por qualquer outro que também seja respeitado por todas as funções declaradas da mesma forma. Vou deixar de fora os traços de concha agora - não estamos entrando em nenhum território desconhecido aqui ou algo assim.
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 ;
Acima, declaro duas funções e defino _if_unset
. Agora, antes de chamar qualquer uma das funções, desmarcarei common_param
para que você possa ver que elas próprias definirão quando eu as chamar.
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 agora do escopo do chamador:
echo $common_param
#OUTPUT#
REQ/RESET added to all funcs
Mas agora eu quero que seja algo completamente diferente:
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 eu desarmar _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
REDEFINIR
Se você precisar redefinir o estado da função a qualquer momento, isso é fácil. Você só precisa fazer (de dentro da função):
. /dev/fd/5
Salvei os argumentos usados para declarar inicialmente a função no 5<<\RESET
descritor de arquivo de entrada. Assim, a .dot
fonte que no shell a qualquer momento repetirá o processo que o configurou em primeiro lugar. É tudo muito fácil, realmente e totalmente portátil, se você estiver disposto a ignorar o fato de que o POSIX não especifica realmente os caminhos dos nós dos dispositivos descritores de arquivos (que são uma necessidade para os shell .dot
).
Você pode facilmente expandir esse comportamento e configurar estados diferentes para sua função.
MAIS?
A propósito, isso apenas arranha a superfície. Costumo usar essas técnicas para incorporar pequenas funções auxiliares declaráveis a qualquer momento na entrada de uma função principal - por exemplo, para $@
matrizes posicionais adicionais, conforme necessário. De fato - como acredito, deve ser algo muito próximo disso que as conchas de ordem superior fazem de qualquer maneira. Você pode ver que eles são facilmente nomeados programaticamente.
Também gosto de declarar uma função geradora que aceita um tipo limitado de parâmetro e, em seguida, define uma função de queimador de uso único ou de escopo limitado, ao longo das linhas de uma lambda - ou uma função em linha - que simplesmente unset -f
é a mesma quando através. Você pode passar uma função shell ao redor.
typeset
necessário? Não o declararia de outra forma?