Bash è una riscrittura da zero della Bourne shell, fatta per il progetto GNU per dare ai sistemi liberi un interprete di comandi che non dipendesse dal codice proprietario di AT&T. Da qui — rimpiazzare un programma esistente senza cambiarne il comportamento osservabile — discendono gran parte delle scelte tecniche di Bash, comprese quelle che oggi rendono più complicato scrivere script portabili.
Contesto
La Bourne shell (sh) di Stephen Bourne, distribuita con la Seventh Edition Unix nel 1979, è per anni l’interprete di riferimento per gli script di sistema. Il suo codice ricade sotto la licenza Unix di AT&T e non si può ridistribuire liberamente. Il progetto GNU ricostruisce dal 1984 un sistema operativo completo a partire da componenti liberi, e una shell è un tassello obbligato.
Brian Fox comincia a scrivere Bash nel 1988 e pubblica la prima beta, la 0.99, l’8 giugno 1989 (articolo di Chet Ramey). Il nome — Bourne-Again SHell — dice l’obiettivo: rifare la Bourne shell, non estenderne il sorgente. La manutenzione passa a Chet Ramey tra il 1992 e il 1994, e Ramey ne è tuttora il manutentore principale.
La versione stabile attuale è Bash 2.05a, annunciata nel novembre 2001 (info-gnu). È soprattutto una release di correzione, un passaggio intermedio prima del lavoro su funzionalità più grosse.
Il problema della riscrittura
Riscrivere un linguaggio mai documentato in modo formale vuol dire inseguire un bersaglio mobile. Lo standard pubblico per il comportamento della Bourne shell non esisteva: a definirlo era ciò che faceva il codice di Bourne, casi limite inclusi. Chi scriveva script si appoggiava a quei dettagli, anche quando non erano voluti.
A cambiare le carte è POSIX.2 (IEEE Std 1003.2-1992), approvato dal comitato IEEE il 17 settembre 1992 (IEEE). Definisce un linguaggio di shell e un insieme di utility, prende come base la Bourne shell di System V e tiene insieme il codice già scritto per le varianti derivate da Bourne. Per la prima volta c’è un riferimento scritto rispetto al quale una riscrittura può dichiararsi conforme, invece di limitarsi a imitare un binario.
Il rapporto di Bash con POSIX.2 è dichiarato apertamente: implementa lo standard e ci aggiunge sopra delle estensioni. La modalità POSIX, che si attiva con l’opzione --posix o impostando la variabile POSIXLY_CORRECT, riallinea allo standard una serie di comportamenti che per default Bash gestisce a modo suo. Che questa modalità debba esistere dice già il problema: Bash deve servire due padroni, la conformità allo standard e l’utilità interattiva di tutti i giorni, e i due obiettivi non sempre vanno d’accordo.
Estensioni e portabilità
Le funzionalità che Bash 2.x aggiunge alla Bourne shell sono quelle che rendono comodo lo scripting vero: gli array monodimensionali, l’espansione aritmetica con $(( )), il pattern matching esteso, la process substitution <(comando) che presenta l’output di un comando come se fosse un file, l’editing della riga di comando attraverso la libreria readline. Nessuna di queste fa parte di POSIX.2.
Da qui la decisione concreta che ogni autore di script si trova davanti. Uno script che usa array e process substitution gira su Bash e si pianta su una shell POSIX minimale, come quella che molti sistemi installano come /bin/sh. Uno script che resta dentro il sottoinsieme POSIX gira ovunque, ma rinuncia a costrutti che renderebbero il codice più corto e più difficile da sbagliare.
Il caso più insidioso è la riga #!/bin/sh. Su molte distribuzioni GNU/Linux /bin/sh è un collegamento simbolico a Bash, e Bash chiamato come sh entra da solo in modalità POSIX. Uno script con shebang #!/bin/sh che gira bene sulla macchina di chi lo scrive può così usare estensioni Bash senza accorgersene, e poi piantarsi su un sistema dove /bin/sh punta a un’altra shell. La colpa non è del codice: è lo scarto tra l’interprete che si crede di chiamare e quello che si chiama davvero. La regola che ne discende è dichiarare la dipendenza con onestà: #!/bin/bash quando si usano le estensioni, #!/bin/sh solo quando il corpo dello script resta dentro POSIX.2.
Implicazioni
La distinzione tra standard ed estensione non è un dettaglio da manuale per chi amministra sistemi. Decide quali macchine eseguono un dato script senza ritocchi. Uno script di backup, di rotazione dei log o di provisioning scritto contro POSIX.2 sopravvive al cambio di shell predefinita, al passaggio a un sistema non Linux, all’esecuzione dentro un ambiente minimale dove Bash non c’è.
C’è anche una conseguenza che tocca la natura libera dello strumento. Bash è distribuito sotto GNU General Public License: il sorgente è disponibile, il comportamento si ispeziona, le deviazioni dallo standard stanno scritte nel manuale e nel file CHANGES. Quando uno script si comporta in modo strano, la causa si verifica leggendo il codice o la documentazione, non si deduce per tentativi contro un binario opaco. Dove prima c’era un comportamento osservabile ma non documentato, la riscrittura libera ne ha messo uno documentato e modificabile.
Limiti
Lo standard di riferimento è in movimento. Il 30 gennaio 2002 l’Austin Group — il gruppo congiunto di IEEE, ISO/IEC JTC 1 e The Open Group — pubblica la revisione combinata nota come IEEE Std 1003.1-2001 e Single UNIX Specification versione 3 (The Open Group), che riunisce in un unico testo le parti prima separate di POSIX. Bash 2.05a precede questa pubblicazione di poche settimane: la sua conformità si misura contro POSIX.2 del 1992, e l’allineamento al testo nuovo richiederà tempo. Scrivere “script POSIX” nel 2002 obbliga allora a specificare contro quale edizione dello standard, perché ne esistono due e i dettagli divergono.
Resta vero anche che la portabilità ha un costo. Il sottoinsieme POSIX è più verboso e meno espressivo del Bash completo, e la scelta di rinunciare alle estensioni va giustificata caso per caso. Per uno script che girerà solo su macchine sotto il proprio controllo, dove /bin/bash c’è di sicuro, l’aderenza stretta allo standard è fatica buttata. La regola non è spingere la portabilità al massimo in assoluto, ma sapere quale livello serve e dichiararlo nel codice.
https://www.gnu.org/software/bash/ https://lists.gnu.org/archive/html/info-gnu/2001-11/msg00004.html http://tiswww.case.edu/php/chet/bash/article.pdf https://standards.ieee.org/ieee/1003.2/1408/ https://pubs.opengroup.org/onlinepubs/009695399/ https://www.noze.it/insights/bash-open-source/
Immagine di copertina: Brian Fox, autore di Bash, ritratto a mezzo busto: uomo con barba e occhiali — foto di Lissa Liggett, CC BY 3.0 — https://commons.wikimedia.org/wiki/File:BrianJFox.png