Escrevendo um script contendo apenas uma definição de função v.s. movendo o código no corpo da função para o script?

2

Alguns antecedentes: Ao escrever código reutilizável no bash, vi alguns scripts de shell contendo várias definições de funções . Às vezes não consigo descobrir se as funções definidas em um script estão relacionadas ou se o script é realmente uma bagunça ou se ainda não sei como organizar várias funções em um script (quais são suas dicas para esse propósito?) . Então, estou pensando em escrever um script contendo apenas uma definição de função, e acredito que terei que organizar vários arquivos. Qual deles é melhor, organizando várias definições de função em um script ou organizando vários arquivos de script, cada um com uma definição de função?

A seguir, assumirei apenas escrever um script contendo apenas uma definição de função.

As minhas perguntas reais começam aqui:

Ao escrever um script contendo apenas uma definição de função , posso escrever

#! /bin/bash

function myfunc() {
    echo $1, $2, $3
    # do some work...
}

e depois eu posso usá-lo por

source /path/to/myscript
myfunc 1 2 3

Como alternativa, posso alcançar o mesmo objetivo de reusabilidade de código, eliminando a definição de função e movendo o código no corpo da função para o script:

#! /bin/bash

echo $1, $2, $3
# do some work...

e depois eu posso usá-lo por

source /path/to/myscript 1 2 3

A segunda maneira parece mais concisa do que a primeira, em termos de conteúdo e uso do script, mas não tenho certeza se alguém pode não gostar da segunda maneira por algum bom motivo. Eu estava me perguntando o que algumas vantagens e desvantagens das duas formas acima são? Qual é a sua recomendação na prática?

Se você usar a primeira maneira na prática, nomearia seu script da mesma forma que sua função, ou seja, se o seu script contiver uma função chamada myfunc , você nomeará o script como myfunc também?

Obrigado.

    
por Tim 14.11.2018 / 17:52

2 respostas

3

Eu faço ambos dependendo da circunstância. Na maioria dos casos, declararei as funções no mesmo script que as utilizarei, no entanto, tenho um "kit de ferramentas" de scripts que tem um arquivo de variáveis e funções a serem compartilhadas entre todos os scripts.

Para funcionar ou não funcionar

O principal objetivo de uma função é DRY (não se repetir). Se você tiver algum código que será usado mais de uma vez no seu script, ele deve estar em uma função.

O uso de funções em código não repetitivo é uma questão de preferência. Algumas pessoas (inclusive eu) consideram que é mais puro. Além disso, acho que se assemelha mais ao modo como as linguagens de programação "reais" são usadas.

O Manual de estilo da Shell do Google afirma que, se o seu código contiver pelo menos uma função, você deverá usar um "principal" funciona como um wrapper para todo o seu código. (Essencialmente, o seu script será apenas uma série de declarações de função, terminando com uma única chamada para main )

Declarando funções (ou escrevendo código) dentro de um único script

  • Simples (qualquer um que procure pelo código poderá rastrear mais facilmente o que cada função faz)
  • Aumenta a portabilidade (somente um arquivo precisa ser movido de sistema para sistema)

Se você estiver escrevendo um script autônomo para realizar uma única tarefa, esse provavelmente é o caminho a ser seguido.

Declarando funções dentro de um arquivo separado

  • DRY (não se repita)
  • Pode ser mais fácil de gerenciar

Se você estiver criando um conjunto de ferramentas que compartilham muitas funções comuns, esse é provavelmente o caminho a seguir.

No meu exemplo, tenho funções que são usadas por todos ou pelo menos a maioria dos scripts no kit de ferramentas. Isso inclui algumas funções que consultam nosso sistema de gerenciamento de inventário e retornam informações sobre servidores, como endereço IP, versão do SO, etc. Essas informações são úteis para muitas das ferramentas, portanto, faz sentido declarar essas funções em um arquivo em vez de em todas as arquivos individualmente. Além disso, recentemente fizemos alterações no sistema de gerenciamento de inventário, de modo que, em vez de ter que alterar mais de 10 arquivos diferentes, eu só tive que alterar o arquivo comum para consultar o novo sistema, os outros arquivos ainda funcionam corretamente sem modificações.

Algumas desvantagens disso são que é mais complexo. Cada arquivo tem a seguinte instrução para originar este arquivo comum:

if [[ -f "${0%/*}/lib/common.sh" ]]; then
    . "${0%/*}/lib/common.sh"
else
    echo "Error! lib/common.sh not found!"
    exit 1
fi

Se os usuários pegarem o kit de ferramentas e modificarem a estrutura de diretórios ou não agarrarem toda a estrutura de diretórios, as ferramentas não funcionarão como deveriam, porque não poderão originar o arquivo comum.

    
por 14.11.2018 / 18:27
0

Esta será uma resposta longa e não do tipo sim ou não. Ele descreve diferentes aspectos de programação que as funções Bash cobrem, que devem ser levadas em consideração durante a escolha da linha de ação.

No final, você pode encontrar uma demonstração de uma única definição de função em um arquivo separado, mas parametrizado, de origem.

Uma função é:

  • um chamado
  • bloquear de código
  • que pode ser reutilizado (chamado muitas vezes)

Atendendo ao que precede, para que serve e tem alternativas?

  1. Organização lógica do código

    Agrupar um lote de comandos destinados a um determinado propósito faz muito sentido. Afinal, esse é um dos significados do termo função - algo tem tal e tal função . O mesmo objetivo pode ser alcançado de outras maneiras embora:

    • Os comandos podem ser agrupados visualmente, por exemplo. ocupando linhas consecutivas e separado do resto do código com linhas vazias e comentários. Ou apenas coloque na mesma linha com um comentário seguindo no final.
    • Eles podem ser agrupados em um arquivo separado, que pode ser executado ou originado .
    • Eles podem ser bloqueados usando as chaves { ... } .
    • Eles podem até ser agrupados e executados em sua subpela separada , quando colocados dentro dos parênteses sub-shell ( ... ) . 1
  2. Operações em lote

    As funções oferecem a vantagem de um fácil redirecionamento de E / S em massa com os operadores padrão do Bash. Blocos, subshells, execução de scripts e sourcing também, mas para os grupos de comandos meramente visuais, as alterações do descritor de arquivo de E / S devem ser aplicadas com exec .

  3. Localização

    Esta é uma das coisas que tornam as funções especiais em oposição a blocos simples. Ser capaz de fazer as variáveis local . E isso geralmente é uma boa prática de programação, pois mantém a visibilidade da variável em seu nível mais baixo, o que reduz a desordem no namespace mais amplo e introduz isolamento. O que, por sua vez, é um recurso de segurança valioso.

    Scripts separados e subshells são uma alternativa aqui. Arquivos de origem também, no que diz respeito aos parâmetros posicionais.

  4. Parametrização

    Cada função obtém seu próprio conjunto de parâmetros posicionais $1 , $2 , etc. Isso permite que o comportamento das funções seja modificado de maneira explícita na chamada de comando.

    As alternativas aqui incluem scripts autônomos e arquivos originados.

  5. Identificação do código

    O nome da função é único, e isso significa não apenas o reconhecimento não ambíguo, mas também a capacidade de ser executado. E muitas vezes de necessidade. Nem os grupos {} , nem o subshell, nem o texto simples oferecem isso - a menos que sejam abraçados por loops. Apesar de scripts e arquivos originados fazerem.

  6. Informações

    Por fim, o nome da função e quaisquer comentários que ele possa conter explicam a inteligibilidade do código. Todas as outras opções apresentam margem de manobra aqui, mas soluções anônimas como o subshell e o bloco {} não podem facilitar um título formal (e endereço) para um bloco de código.

Veja um exemplo de como uma única função pode ser razoavelmente colocada em um arquivo de origem separado.

case "$1" in
    a)
        f () { echo "This function was defined in a)."; }
        ;;
    b)
        f () { echo "This function was defined in b)."; }
        ;;
    *)
        f () { echo "This function was defined with no recognised argument."; }
        ;;
esac

Esta definição condicional age como uma fábrica de funções. O arquivo originado de outro local instala a função f lá. Todo esse código pode estar incluído em outra função e, da maneira como está, haveria pouca diferença. Mas, uma vez que o gerador de funções seja grande o suficiente para se tornar uma entidade em si, essa abordagem combina o melhor dos dois: a função e o arquivo originado.

Veja também

Na verdade, o Bash permite uma definição de função similar: f () ( ...;) . Assim definida, a função f será executada em sua própria sub-rede.

    
por 21.11.2018 / 22:10