Em python, podemos decorar funções com código que é automaticamente aplicado e executado em funções.
Existe algum recurso semelhante no bash?
No script em que estou trabalhando, tenho alguns boilerplate que testam os argumentos necessários e saem se eles não existirem - e exibem algumas mensagens se o sinalizador de depuração for especificado.
Infelizmente, tenho que reinserir esse código em todas as funções e, se quiser alterá-lo, terei que modificar todas as funções.
Existe uma maneira de remover esse código de cada função e aplicá-lo a todas as funções, semelhante aos decoradores em python?
typeset
necessário? Não o declararia de outra forma?eval "_inner_$(typeset -f x)"
cria_inner_x
como uma cópia exata do originalx
(igual afunctions[_inner_x]=$functions[x]
emzsh
).return
.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.
EXECUTÁ-LO
Aqui apelo
_fn_init
para me declarar uma função chamadafn
.REQUERIDOS
Se eu quiser chamar essa função, ela morrerá, a menos que a variável de ambiente
_if_unset
esteja definida.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.
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.Acima, declaro duas funções e defino
_if_unset
. Agora, antes de chamar qualquer uma das funções, desmarcareicommon_param
para que você possa ver que elas próprias definirão quando eu as chamar.E agora do escopo do chamador:
Mas agora eu quero que seja algo completamente diferente:
E se eu desarmar
_if_unset
?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):
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.fonte
eval
?.dot
funciona com arquivos e fluxos, para que você não tenha o mesmo tipo de problemas da lista de argumentos que poderia. Ainda assim, é provavelmente uma questão de preferência. Eu certamente acho que é mais limpo - especialmente quando você faz uma avaliação - é um pesadelo de onde eu sento..dot
até que esteja bom e pronto - ou nunca. Isso permite um pouco mais de liberdade ao testar suas avaliações. E fornece a flexibilidade do estado na entrada - que pode ser tratada de outras maneiras -, mas é muito menos perigoso dessa perspectiva do que éeval
.Eu acho que uma maneira de imprimir informações sobre a função, quando você
é alterar o bash interno
return
e / ouexit
no início de cada script (ou em algum arquivo que você sempre utiliza antes de executar o programa). Então você digitaSe você executar isso, receberá:
Isso pode ser facilmente atualizado com o sinalizador de depuração, se você precisar, mais ou menos assim:
Essa instrução way será executada apenas quando a variável VERBOSE estiver configurada (pelo menos é assim que eu uso o verbose nos meus scripts). Certamente não resolve o problema da função de decoração, mas pode exibir mensagens caso a função retorne um status diferente de zero.
Da mesma forma, você pode redefinir
exit
, substituindo todas as instâncias dereturn
, se desejar sair do script.Edição: Eu queria adicionar aqui a maneira que eu uso para decorar funções no bash, se eu tiver muitos deles e aninhados também. Quando escrevo este script:
E para a saída eu posso conseguir isso:
Pode ser útil para alguém que possui funções e deseja depurá-las, para ver em qual erro de função ocorreu. É baseado em três funções, que podem ser descritas abaixo:
Eu tentei colocar o máximo possível em comentários, mas aqui é também a descrição: Eu uso
_ ()
função como decorador, o que eu colocar após a declaração de cada função:foo () { _
. Esta função imprime o nome da função com o recuo apropriado, dependendo da profundidade da função em outra função (como recuo padrão, eu uso 4 números de espaços). Costumo imprimir em cinza, para separar da impressão usual. Se a função precisar ser decorada com argumentos, ou sem, pode-se modificar a pré-última linha na função decorador.Para imprimir algo dentro da função, introduzi a
print ()
função que imprime tudo o que é passado com o recuo apropriado.A função
set_indentation_for_print_function
faz exatamente o que significa, calculando o recuo da${FUNCNAME[@]}
matriz.Dessa forma, existem algumas falhas, por exemplo, não se pode passar opções para
print
gostarecho
, por exemplo ,-n
ou-e
, e também se a função retornar 1, ela não será decorada. E também para argumentos passados paraprint
mais do que a largura do terminal, que serão quebrados na tela, não se verá o recuo da linha quebrada.A melhor maneira de usar esses decoradores é colocá-los em um arquivo separado e em cada novo script para originar esse arquivo
source ~/script/hand_made_bash_functions.sh
.Eu acho que a melhor maneira de incorporar o decorador de funções no bash é escrever decorador no corpo de cada função. Eu acho que é muito mais fácil escrever função dentro da função no bash, porque ele tem a opção de definir todas as variáveis globais, não como nas Linguagens Orientadas a Objetos padrão. Isso faz com que você esteja colocando rótulos em seu código no bash. Pelo menos isso me ajudou em scripts de depuração.
fonte
Talvez os exemplos do decorador em http://sourceforge.net/projects/oobash/ project possam ajudá-lo (oobash / docs / examples / decorator.sh).
fonte
Para mim, isso parece a maneira mais simples de implementar um padrão decorador dentro do bash.
fonte