É comum dividir scripts maiores em vários scripts e originá-los no script principal?

23

No momento, estou desenvolvendo um script Bash maior (é um projeto meu de código aberto) e está começando a se tornar uma bagunça. Dividi a lógica em funções, use variáveis ​​locais onde posso e declarei apenas algumas variáveis ​​globais. Ainda assim, está se tornando muito difícil de manter.

Pensei em dividir o script em vários scripts e originá-los no meu script principal (semelhante às importações em outros idiomas).

Mas me pergunto se essa é uma abordagem viável. Primeiro, o fornecimento de vários scripts pode diminuir bastante o tempo de execução do script e, segundo, torna a distribuição mais difícil.

Então, essa é uma boa abordagem e outros projetos (de código aberto) fazem da mesma maneira?

helpermethod
fonte
4
Procurei um script shell muito longo e, com 3500 linhas e 125 KB, não gostaria de tentar mantê-lo. O shell é realmente amigável ao conectar programas, mas quando tenta fazer cálculos, fica muito feio. Eu sei que o código que você tem mais funciona e os custos de portá-lo são altos, mas você pode considerar outra coisa no futuro.
precisa
1
Eu encontrei o mesmo problema. Eu tenho um projeto bash moderadamente grande sourceforge.net/projects/duplexpr . Atualmente, cada script é independente, mas estou pensando em mover todas as funções comuns para um (s) arquivo (s) separado (s) e incluí-las onde for necessário para evitar a necessidade de atualizá-las em vários locais. Em geral, acho que seria melhor apenas chamar cada script sucessivo do que originá-lo. Isso possibilita o funcionamento independente das peças. Então, você tem que passar variáveis como argumentos ou em arquivos de parâmetros (ou você pode simplesmente exportá-los se você não quiser independente.)
Joe

Respostas:

13

Sim, é uma prática comum. Por exemplo, nos primeiros dias do Unix, o código do shell responsável por guiar o sistema através de suas fases de inicialização até a operação multiusuário era um único arquivo /etc/rc,. Hoje, o processo de inicialização é controlado por muitos scripts de shell, divididos por função, com funções e variáveis ​​comuns originadas conforme necessário em locais centrais. Distribuições Linux, Macs, BSDs, todos adotaram essa abordagem em graus variados.

Kyle Jones
fonte
2
embora, nesse caso, seja mais para reutilização. Scripts diferentes usam uma biblioteca comum de funções de shell.
Stéphane Chazelas
16

O shell é a ferramenta certa para o trabalho nesse ponto? Como desenvolvedor que se deparou com problemas de código de superação, posso dizer que uma reescrita não deve ser considerada, mas, em vez disso, considerar a separação de peças em algo mais adequado para a escala que você deseja aumentar seu aplicativo - talvez python, ruby ​​ou perl ?

O Shell é uma linguagem de utilitário - é uma linguagem de script - e, portanto, será difícil expandi-lo para esses tamanhos.

JasonG
fonte
Isso é exatamente o que eu estava prestes a postar.
Zwol
especialmente considerando que mesmo os sistemas operacionais mais antigos, como o Solaris, são enviados com perl, python etc., agora. Uma das razões pelas quais sistemas mais antigos usavam scripts de shell é que o shell estava sempre disponível, mas Unices maiores, como HP-UX e Solaris e AIX, não podiam garantir outras ferramentas.
Tim Kennedy
7

Se facilitar sua manutenção, você pode ter os dois. Divida-o em partes lógicas para que você possa mantê-lo facilmente e, em seguida, escreva (por exemplo) um Makefile para reunir tudo para distribuição. Você pode escrever alguns scripts rápidos para copiar as funções do arquivo de inclusão para o arquivo de saída no lugar de na sourcelinha ou apenas faça algo trivial como este (você terá que re-tabificar isso, conforme makenecessário)

all: myscript

myscript: includes/* body/*
    cat $^ > "$@" || (rm -f "$@"; exit 1)

Você tem uma versão "fonte" (usada para edição) e uma versão "binária" (usada para instalação trivial).

derobert
fonte
1
Aha! Alguém que usa a abordagem simples gato :)
Clayton Stanley
4

Um script pode ser dividido conforme você descreve - quase tudo pode ser feito. Eu diria que a 'boa abordagem' seria compartimentar seu script grande, descobrir onde partes dele poderiam ser executadas como um processo separado, comunicando-se através dos mecanismos do IPC.

Além disso, para um script de shell, eu o empacotaria como um único arquivo. Como você diz, isso dificulta a distribuição: você precisa saber onde os scripts da 'biblioteca' estão localizados - não há um bom padrão para scripts de shell - ou confiar no usuário para definir seu caminho corretamente.

Você pode distribuir um programa de instalação que lida com tudo isso para você, extraindo os arquivos, colocando-os no local apropriado, pedindo ao usuário para adicionar algo parecido export PROGRAMDIR=$HOME/lib/PROGRAMcom o arquivo ~ / .bashrc. Em seguida, o programa principal poderá falhar se $PROGRAMDIRnão estiver definido ou não contiver os arquivos que você espera.

Eu não me preocuparia tanto com a sobrecarga de carregar os outros scripts. A sobrecarga é realmente apenas abrir um arquivo; o processamento do texto é o mesmo, especialmente se forem definições de função.

Arcege
fonte
1

Prática comum ou não, não acho que terceirizar nada além de um conjunto de exportações seja uma boa idéia. Acho que a execução do código de fornecimento é apenas confusa e limita a reutilização, porque as configurações ambientais e outras variáveis ​​tornam o código de origem altamente dependente do código de fornecimento.

É melhor dividir seu aplicativo em scripts menores e independentes e executá-los como uma série de comandos. Isso facilitará a depuração, pois você pode executar cada script independente em um shell interativo, examinando arquivos, logs, etc. entre cada chamada de comando. Seu script de aplicativo grande e único se transforma em um script de controle mais simples, que executa apenas uma série de comandos após a depuração.

Um grupo de scripts menores e independentes se depara com o problema mais difícil de instalar.

Bruce Ediger
fonte
1

Uma alternativa ao fornecimento dos scripts é simplesmente chamá-los com argumentos. Se você já dividiu a maior parte de sua funcionalidade em funções shell, provavelmente já está quase pronto para fazer isso. O seguinte snippet do Bash permite que qualquer função declarada em um script seja usada como subcomando:

if [[ ${1:-} ]] && declare -F | cut -d' ' -f3 | fgrep -qx -- "${1:-}"
then "$@"
else main "$@" # Try the main function if args don't match a declaration.
fi

A razão para não fazê-lo sourceé evitar a poluição do meio ambiente e das opções.

solidsnack
fonte
0

Aqui está o meu exemplo de como um script bash grande pode ser dividido em vários arquivos e, em seguida, incorporado em um script resultante: https://github.com/zinovyev/bash-project

Eu uso um Makefilepara este fim:

TARGET_FILE = "target.sh"
PRJ_SRC = "${PWD}/src/main.sh"
PRJ_LIB = $(shell ls -d ${PWD}/lib/*) # All files from ./lib

export PRJ_LIB

SHELL := /bin/env bash
all: define_main add_dependencies invoke_main

define_main:
    echo -e "#!/usr/bin/env bash\n" > ${TARGET_FILE}
    echo -e "function main() {\n" >> ${TARGET_FILE}
    cat "${PRJ_SRC}" | sed -e 's/^/  /g' >> ${TARGET_FILE}
    echo -e "\n}\n" >> ${TARGET_FILE}

invoke_main:
    echo "main \$$@" >> ${TARGET_FILE}

add_dependencies:
    for filename in $${PRJ_LIB[*]}; do cat $${filename} >> ${TARGET_FILE}; echo >> ${TARGET_FILE}; done
zinovyev
fonte