È comune dividere script più grandi in più script e crearli nello script principale?


23

Al momento sto sviluppando uno script Bash più grande (è un mio progetto Open Source) e sta iniziando a diventare un casino. Ho diviso la logica in funzioni, uso variabili locali dove posso e ho dichiarato solo una manciata di variabili globali. Tuttavia, sta diventando piuttosto difficile da mantenere.

Ho pensato di dividere lo script in più script e di acquistarli nella mia sceneggiatura principale (simile alle importazioni in altre lingue).

Ma mi chiedo se questo sia un approccio fattibile. Innanzitutto, l'approvvigionamento di più script potrebbe rallentare notevolmente il tempo di esecuzione dello script e, in secondo luogo, rendere più difficile la distribuzione.

Quindi, è un buon approccio e altri progetti (Open Source) lo fanno allo stesso modo?


4
Sono andato alla ricerca di uno script shell davvero lungo e con 3500 righe e 125 KB, non vorrei provare a mantenerlo. La shell è davvero amichevole quando si collegano i programmi, ma quando cerca di fare calcoli, diventa piuttosto brutta. So che il codice che hai principalmente funziona e i costi di porting sono alti, ma potresti voler considerare qualcos'altro in futuro.
msw

1
Ho riscontrato lo stesso problema. Ho un progetto bash moderatamente grande sourceforge.net/projects/duplexpr . Attualmente, ogni script è autonomo, ma sto pensando di spostare tutte le funzioni comuni in uno o più file separati e includerli dove necessario per evitare di doverli aggiornare in più punti. In generale, penso che sarebbe meglio chiamare solo ogni script successivo anziché procurarselo. Rende possibile l'esecuzione indipendente delle parti. Quindi, devi passare le variabili come argomenti o nei file dei parametri (oppure puoi semplicemente esportarle se non vuoi autonomamente.)
Joe,

Risposte:


13

Sì, è una pratica comune. Ad esempio, nei primi giorni di Unix codice shell responsabile per guidare il sistema attraverso le sue fasi di avvio al funzionamento multiutente era un singolo file, /etc/rc. Oggi il processo di avvio è controllato da molti script di shell, suddivisi per funzione, con funzioni e variabili comuni provenienti da posizioni centrali. Le distribuzioni Linux, i Mac, i BSD hanno adottato questo approccio in varia misura.


2
anche se in quel caso, è più per riusabilità. Script diversi usano una libreria comune di funzioni shell.
Stéphane Chazelas,

16

Shell è lo strumento giusto per il lavoro a quel punto? Come sviluppatore che si è imbattuto in problemi di codice in crescita, posso dirti che una riscrittura non dovrebbe essere presa in considerazione ma invece considerare di separare i pezzi in qualcosa di più adatto alla scala che stai cercando di far crescere la tua applicazione - forse python o ruby ​​o persino perl ?

Shell è un linguaggio di utilità - è un linguaggio di scripting - e quindi sarà difficile farlo crescere in quelle dimensioni.


Questo è esattamente quello che stavo per pubblicare.
zwol,

soprattutto dato che anche i sistemi operativi più vecchi come Solaris vengono ora forniti con perl e python, ecc. uno dei motivi per cui i sistemi più vecchi utilizzavano gli script di shell è che la shell era garantita per essere sempre disponibile, ma Unices più grandi, come HP-UX e Solaris e AIX, non potevano garantire che includessero altri strumenti.
Tim Kennedy,

7

Se semplifica la manutenzione, puoi avere entrambi. Dividilo in parti logiche in modo da poterlo mantenere facilmente, quindi scrivi (es.) Un Makefile per rimetterlo tutto insieme per la distribuzione. Puoi scrivere alcuni script rapidi per copiare le funzioni dal file include al file di output al posto di la sourcelinea o semplicemente fai qualcosa di banale come questo (dovrai riprogrammare questo, come makerichiede le schede):

all: myscript

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

Quindi hai una versione "sorgente" (usata per l'editing) e una versione "binaria" (usata per un'installazione banale).


1
Aha! Qualcun altro che utilizza il semplice approccio del gatto :)
Clayton Stanley,

4

Una sceneggiatura potrebbe essere spezzata mentre descrivi - praticamente tutto può essere fatto. Direi che il "buon approccio" sarebbe compartimentare la tua sceneggiatura di grandi dimensioni, capire dove parti di essa potrebbero essere eseguite come un processo separato, comunicando attraverso meccanismi IPC.

Oltre a ciò, per uno script di shell, lo impacchetterei come un singolo file. Come dici tu, rende la distribuzione più difficile: o devi sapere dove si trovano gli script 'library' - non esiste uno standard valido per gli script di shell - o fare affidamento sull'utente per impostare correttamente il loro percorso.

Potresti distribuire un programma di installazione che gestisca tutto questo per te, estraendo i file, mettendoli nella posizione corretta, dicendo all'utente di aggiungere qualcosa di simile export PROGRAMDIR=$HOME/lib/PROGRAMal file ~ / .bashrc. Quindi il programma principale potrebbe non funzionare se $PROGRAMDIRnon è impostato o non contiene i file previsti.

Non mi preoccuperei tanto del sovraccarico di caricare gli altri script. L'overhead sta davvero aprendo un file; l'elaborazione del testo è la stessa, soprattutto se si tratta di definizioni di funzioni.


1

Pratica comune o no, non credo che approvvigionare altro che un insieme di esportazioni sia una buona idea. Trovo che l'esecuzione del codice di sourcing sia semplicemente confusa e limita il riutilizzo, poiché le impostazioni ambientali e altre variabili rendono il codice di provenienza altamente dipendente dal codice di provenienza.

È meglio suddividere l'applicazione in script più piccoli e autonomi, quindi eseguirli come una serie di comandi. Ciò faciliterà il debug, poiché è possibile eseguire ogni script autonomo in una shell interattiva, esaminando file, registri, ecc. Tra ogni chiamata di comando. Il tuo singolo script di applicazioni di grandi dimensioni si trasforma in uno script di controllo più semplice che esegue una serie di comandi al termine del debug.

Un gruppo di script più piccoli e autonomi si imbatte nel problema più difficile da installare.


1

Un'alternativa all'approvvigionamento degli script è semplicemente chiamarli con argomenti. Se hai già suddiviso la maggior parte delle tue funzionalità in funzioni shell, probabilmente sei già abbastanza vicino a poterlo fare già. Il seguente frammento di Bash consente di utilizzare come sottocomando qualsiasi funzione dichiarata in uno script:

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

La ragione per non farlo sourceè evitare l'inquinamento ambientale e delle opzioni.


0

Ecco il mio esempio di come uno script bash di grandi dimensioni può essere suddiviso in più file e quindi incorporato in uno script risultante: https://github.com/zinovyev/bash-project

Uso a Makefileper questo scopo:

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.