Troppe righe di shebang (dichiarazione dello script): un modo per ridurne l'importo?


12

Ho un progetto composto da circa 20 piccoli .shfile. Definisco questi "piccoli" perché in genere nessun file ha più di 20 righe di codice. Ho adottato un approccio modulare perché quindi sono fedele alla filosofia Unix ed è più facile per me mantenere il progetto.

All'inizio di ogni .shfile, ho messo #!/bin/bash.

In poche parole, capisco che le dichiarazioni di script hanno due scopi:

  1. Aiutano l'utente a ricordare quale shell è necessaria per eseguire il file (diciamo, dopo alcuni anni senza usare il file).
  2. Garantiscono che lo script venga eseguito solo con una determinata shell (Bash in quel caso) per prevenire comportamenti imprevisti nel caso in cui venga utilizzata un'altra shell.

Quando un progetto inizia a crescere da diciamo 5 file a 20 file o da 20 file a 50 file (non in questo caso, ma solo per dimostrarlo) abbiamo 20 righe o 50 righe di dichiarazioni di script. Lo ammetto, anche se per alcuni potrebbe essere divertente, mi sembra un po 'ridondante usare 20 o 50 invece di dire solo 1 per progetto (forse nel file principale del progetto).

C'è un modo per evitare questa presunta ridondanza di 20 o 50 o un numero molto maggiore di righe di dichiarazioni di script usando una dichiarazione di script "globale", in alcuni file principali?


2
Non dovresti ma potresti; rimuovilo ed esegui tutti i tuoi script con/bin/bash -x "$script"
jesse_b


... Di solito quando qualcosa arriva a questo punto ho già concluso che è ora di smettere di usare gli script di shell e ottenere un vero linguaggio di script per fare il lavoro.
Shadur,

Risposte:


19

Anche se il tuo progetto ora può consistere esclusivamente di 50 script Bash, prima o poi inizierà ad accumulare script scritti in altre lingue come Perl o Python (per i vantaggi che questi linguaggi di scripting hanno che Bash non ha).

Senza una linea appropriata #!in ogni script, sarebbe estremamente difficile usare i vari script senza sapere anche quale interprete usare. Non importa se ogni singolo script viene eseguito da altri script , questo sposta solo la difficoltà dagli utenti finali agli sviluppatori. Nessuno di questi due gruppi di persone dovrebbe aver bisogno di sapere in quale lingua è stata scritta una sceneggiatura per poterla usare.

Gli script di shell eseguiti senza una #!riga e senza un interprete esplicito vengono eseguiti in modi diversi a seconda di quale shell li invoca (vedere ad esempio la domanda Quale interprete di shell esegue uno script senza shebang? E in particolare la risposta di Stéphane ), che non è ciò che si desidera in un ambiente di produzione (si desidera un comportamento coerente e possibilmente anche portabilità).

Gli script eseguiti con un interprete esplicito verranno eseguiti da quell'interprete indipendentemente da ciò che #!dice la linea. Ciò causerà problemi più in là se decidi di reimplementare, per esempio, uno script Bash in Python o qualsiasi altra lingua.

Dovresti spendere quei tasti extra e aggiungere sempre una linea #!a ogni singolo script.


In alcuni ambienti, in ogni sceneggiatura di ciascun progetto sono presenti testi legali a più paragrafi in caldaia. Sii molto felice che sia solo una linea #!che sembra "ridondante" nel tuo progetto.


La risposta dovrebbe essere espansa in questo modo: la shell determina l'interprete per #!riga non appena il file ha un'autorizzazione esplicabile e non viene caricato da un interprete, ma dalla shell. Cioè, /path/to/myprog.plesegue un programma perl se la #!linea è presente (puntando all'interprete perl) e il file ha l'autorizzazione exec.
rexkogitans,

11

Hai frainteso il punto di #!. In realtà è una direttiva per il sistema operativo eseguire questo programma con l'interprete definito.

Quindi potrebbe essere uno script #!/usr/bin/perle il programma risultante può essere eseguito come ./myprograme l'interprete perl potrebbe essere utilizzato. Simile #!/usr/bin/pythonfarebbe girare un programma su Python.

Quindi la linea #!/bin/bashdice al sistema operativo di eseguire questo programma sotto bash.

Ecco un esempio:

$ echo $0
/bin/ksh

$ cat x
#!/bin/bash

ps -aux | grep $$

$ ./x
sweh      2148  0.0  0.0   9516  1112 pts/5    S+   07:58   0:00 /bin/bash ./x
sweh      2150  0.0  0.0   9048   668 pts/5    S+   07:58   0:00 grep 2148

Quindi, nonostante la mia shell sia ksh, il programma "x" funziona sotto bash a causa della #!linea, e possiamo vederlo esplicitamente nell'elenco dei processi.


ps -auxpuò dare avvertimento,
Weijun Zhou

@WeijunZhou Di più ...
Kusalananda

Alcune versioni di psdanno l'avvertimento Warning: bad syntax, perhaps a bogus '-'?.
Weijun Zhou,

2
pssuooprts sintassi degli argomenti sia BSD che UXIX . -auxè sbagliato UNIX Stephen probabilmente significa auxche è corretto BSD
Jasen

1
Persone, 2 cose; (1) concentrarsi sul concetto di esempio (che psmostra la chiamata di bash) non sulla sintassi esplicita; (2) ps -auxfunziona perfettamente su CentOS 7 e Debian 9 senza avvisi; quel taglio era esattamente l'output della mia macchina; l'intera discussione sulla -necessità o meno è applicabile alle versioni precedenti di procps di Linux, non alle versioni attuali.
Stephen Harris,

9

Su .sh

Ciò che è male è .shalla fine del nome file.

Immagina di riscrivere uno degli script in Python.

Bene, ora devi cambiare la prima riga in #!/usr/bin/python3questo non è poi così male, dato che hai dovuto cambiare anche ogni altra riga di codice nel file. Tuttavia, devi anche cambiare il nome del file da prog.sha prog.py. E poi devi trovare ovunque negli altri script e tutti i tuoi script utente (quelli che non sapevi nemmeno esistessero) e cambiarli per usare il nuovo script. sed -e 's/.sh/.py/g'può aiutare per quelli che conosci. Ma ora il tuo strumento di controllo delle revisioni sta mostrando un cambiamento in molti file non correlati.

In alternativa farlo nel modo di Unix e il nome del programma prognon prog.sh.

Su #!

Se si dispone del metodo corretto #!e impostare l'autorizzazione / modalità su Includi Esegui. Quindi non è necessario conoscere l'interprete. Il computer lo farà per te.

chmod +x prog #on gnu adds execute permission to prog.
./prog #runs the program.

Per i sistemi non gnu leggi il manuale di chmod(e impara l'ottale), magari leggilo comunque.


1
Hmmm, le estensioni non sono necessariamente dannose, almeno aiutano a comunicare con altri utenti qual è il tipo di file.
Sergiy Kolodyazhnyy,

@SergiyKolodyazhnyy Ma non devi conoscere la lingua, devi solo eseguirla. Anche Microsoft sta cercando di allontanarsi dalle estensioni (purtroppo lo stanno facendo nascondendole). Comunque concordo sul fatto che potrebbero essere una buona idea: .imageper le foto. .audioper il suono, ecc. Ti dice così di cosa si tratta ma non sull'implementazione / codifica.
ctrl-alt-delor,

1
@ ctrl-alt-delor Se il file viene chiamato launch-missileso delete-project-and-make-manager-pissednon voglio "semplicemente eseguirlo" quando ero solo curioso della lingua :)
Sergiy Kolodyazhnyy

2
se sei curioso per la lingua usa il filecomando riconosce la maggior parte dei tipi di file.
Jasen,

2
Concordo sul fatto che le estensioni sono dannose per gli script eseguibili, per i motivi indicati. Uso un'estensione .sh per uno script che deve essere fornito da un altro script shell (ovvero uno script non eseguibile) - in quello scenario, il suffisso è importante / valido perché ci dice come possiamo usare il file.
Laurence Renshaw,

8

[Le linee hashbang] assicurano che lo script venga eseguito solo con una determinata shell (Bash in quel caso) per prevenire comportamenti imprevisti nel caso in cui fosse usata un'altra shell.

Probabilmente vale la pena sottolineare che questo è abbastanza sbagliato. La linea hashbang non impedisce in alcun modo di provare a inviare il file a una shell / interprete errata:

$ cat array.sh
#!/bin/bash
a=(a b c)
echo ${a[1]} $BASH_VERSION
$ dash array.sh
array.sh: 2: array.sh: Syntax error: "(" unexpected

(o in modo simile con awk -f array.shecc.)


Ma in ogni caso, un altro approccio alla modularità sarebbe quello di definire le funzioni di shell nei file, una funzione per file, se lo si desidera, e quindi sourcei file dal programma principale. In questo modo, non avresti bisogno di hashbang: sarebbero trattati come qualsiasi altro commento e tutto verrebbe eseguito usando la stessa shell. Il rovescio della medaglia sarebbe che avresti bisogno sourcedei file con le funzioni (ad es. for x in functions/*.src ; do source "$x" ; done), E che tutti funzionerebbero usando la stessa shell, quindi non potresti sostituire direttamente uno di loro con un'implementazione Perl.


+1 ad esempio con array bash. Gli utenti che non hanno familiarità con più shell o con alcune delle definizioni di POSIX, possono presumere che alcune funzioni di una shell esistano in un'altra. Anche per le funzioni, questo potrebbe essere rilevante: unix.stackexchange.com/q/313256/85039
Sergiy Kolodyazhnyy,

4

C'è un modo per evitare questa presunta ridondanza di 20 o 50 o un numero molto maggiore di righe di dichiarazioni di script usando una dichiarazione di script "globale", in alcuni file principali?

C'è - si chiama portabilità o conformità POSIX. Sforzati di scrivere sempre script in modo portatile, indipendente dalla shell, procurandoli solo da uno script che usi #!/bin/sho quando lo usi in modo /bin/shinterattivo (o almeno una shell conforme a POSIX come ksh).

Tuttavia, gli script portatili non ti salvano da:

  • necessità di utilizzare le funzionalità di una delle shell ( la risposta di ilkkachu è un esempio)
  • necessità di utilizzare linguaggi di scripting più avanzati rispetto alle shell (Python e Perl)
  • Errore di PEBKAC: lo script cade nelle mani di J.Doe utente che esegue lo script non come si voleva che, ad esempio, che corrono /bin/shscript con csh.

Se posso esprimere la mia opinione, "evitare la ridondanza" eliminando #!è un obiettivo sbagliato. L'obiettivo dovrebbe essere una prestazione coerente e in realtà uno script funzionante che funzioni e consideri i casi angolari, segue alcune regole generali come non-parsing-ls o citando sempre-variabili-a meno che non sia necessario-dividere le parole .

Aiutano l'utente a ricordare quale shell è necessaria per eseguire il file (diciamo, dopo alcuni anni senza usare il file).

Non sono davvero per l'utente - sono per il sistema operativo per eseguire l'interprete corretto. "Corretto" in questo caso significa che il sistema operativo trova ciò che è in shebang; se il codice sottostante contiene una shell errata, è colpa dell'autore. Per quanto riguarda la memoria dell'utente, non penso che "utente" di programmi come Microsoft Word o Google Chrome abbia mai avuto bisogno di sapere in quali lingue sono scritti i programmi; gli autori potrebbero aver bisogno.

Inoltre, gli script scritti in modo portabile possono eliminare la necessità di ricordare anche quale shell è stata utilizzata originariamente, poiché gli script portatili dovrebbero funzionare in qualsiasi shell conforme a POSIX.

Garantiscono che lo script venga eseguito solo con una determinata shell (Bash in quel caso) per prevenire comportamenti imprevisti nel caso in cui venga utilizzata un'altra shell.

Non lo fanno. Ciò che impedisce effettivamente comportamenti imprevisti è la scrittura di script portatili.


1

Il motivo per cui devi mettere #!/bin/bashin cima a ogni script è che lo stai eseguendo come processo separato.

Quando si esegue uno script come nuovo processo, il sistema operativo rileva che si tratta di un file di testo (al contrario di un eseguibile binario) e deve sapere quale interprete eseguire. Se non lo dici, il sistema operativo può solo indovinare, usando le sue impostazioni predefinite (che un amministratore può cambiare). Quindi la #!linea (oltre all'aggiunta di autorizzazioni eseguibili, ovviamente) trasforma un semplice file di testo in un eseguibile che può essere eseguito direttamente, con la certezza che il sistema operativo sappia cosa farne.

Se si desidera rimuovere la #!linea, le opzioni sono:

  1. Esegui direttamente lo script (come stai facendo ora) e spera che l'interprete predefinito corrisponda allo script: probabilmente sei al sicuro sulla maggior parte dei sistemi Linux, ma non è portatile su altri sistemi (ad esempio Solaris) e può essere modificato in qualsiasi cosa (potrei anche impostarlo per l'esecuzione vimsul file!).

  2. Eseguire lo script utilizzando bash myscript.sh. Funziona bene, ma devi specificare il percorso dello script ed è disordinato.

  3. Origine dello script utilizzando . myscript.sh, che esegue lo script all'interno del processo interprete corrente (ovvero non come sottoprocesso). Ma ciò richiederà quasi sicuramente modifiche ai tuoi script e li renderà meno modulari / riutilizzabili.

Nei casi 2 e 3, il file non ha bisogno (e non dovrebbe avere) di eseguire le autorizzazioni, e dare un .sh(o .bash) suffisso è una buona idea.

Ma nessuna di queste opzioni sembra buona per il tuo progetto, come hai detto che vuoi mantenere i tuoi script modulari (=> indipendenti e indipendenti), quindi dovresti mantenere la tua #!/bin/bashlinea in cima agli script.

Nella tua domanda, hai anche detto che stai seguendo la filosofia Unix. Se questo è vero, allora dovresti imparare a cosa #!serve davvero la linea e continuare a usarla in ogni script eseguibile!


Grazie per la buona risposta Mi è piaciuto tutto nella risposta e ho pensato di upvoting fino a questo: then you should learn what the #! line is really for, and keep using it in every executable script!. Si può seguire la filosofia U. fino a un certo punto di conoscenza. Dopo tutte queste risposte ho capito perché può essere incluso in quel termine. Suggerisco di modificare la risposta, sostituendola con "Vedi lo shebang come parte interna della filosofia Unix che rispetti e adotti".
user9303970

1
Scusate se ero diretto o se quel commento sembrava maleducato. I tuoi commenti suggeriscono che preferisci lavorare in un IDE, con un "progetto" che ti consente di definire regole globali per gli script all'interno di quel progetto. Questo è un modo valido per lo sviluppo, ma non si adatta bene a trattare ogni script come un programma indipendente e indipendente (la filosofia Unix "a cui ti riferivi).
Laurence Renshaw,
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.