Cosa fa#
Sequenza di due caratteri #! all'inizio di uno script che dice al kernel quale interprete usare per eseguirlo. Il kernel legge i primi due byte del file — se trova #! (shebang), usa il path che segue come interprete. Se non c'e', la shell corrente esegue lo script.
TL;DR#
./script.sh
│
▼
kernel legge primi 2 byte
│
├── trova #! → usa l'interprete specificato
│ sempre lo stesso, ovunque
│
└── non trova #! → usa la shell corrente
comportamento dipende dall'ambienteCome funziona#
# Il kernel vede:
#!/bin/bash
echo "ciao"
# Lo trasforma in:
/bin/bash ./script.sh
# Equivalente a scrivere direttamente:
bash script.shIl #! non e' commento per il kernel — e' una direttiva. Per bash invece # e' un commento, quindi la riga shebang viene ignorata dall'interprete.
Shebang comuni#
#!/bin/bash # bash esplicito
#!/bin/sh # POSIX puro — usa sh (spesso dash su Ubuntu)
#!/usr/bin/env python3 # python3 — cerca nel PATH
#!/usr/bin/env node # node.js — cerca nel PATH/usr/bin/env e' un pattern importante — invece di hardcodare il path dell'interprete, cerca nel PATH dell'utente. Piu' portabile tra sistemi diversi dove python3 potrebbe stare in /usr/local/bin/ invece di /usr/bin/.
Senza shebang — comportamento non portabile#
# script.sh senza shebang
x=10
echo $xSei in bash: ./script.sh → bash lo esegue → funziona
Sei in zsh: ./script.sh → zsh lo esegue → funziona
Sei in dash: ./script.sh → dash lo esegue → potrebbe fallire
se usi bash-specificRisultato dipendente dall'ambiente — non portabile.
Shebang e variabili — serve sempre export#
Indipendentemente dallo shebang, uno script lanciato con ./ o bash script.sh e' sempre una subshell — un processo figlio separato.
CASO 1 — con shebang #!/bin/bash
─────────────────────────────────────────
shell corrente
│ export MESSAGGIO="ciao" ← esportata
│ LOCALE="solo qui" ← locale, non esportata
│
└─► subshell (bash via shebang)
echo $MESSAGGIO → "ciao" ← eredita
echo $LOCALE → "" ← non eredita
CASO 2 — senza shebang
─────────────────────────────────────────
shell corrente (bash)
│ export MESSAGGIO="ciao" ← esportata
│ LOCALE="solo qui" ← locale, non esportata
│
└─► subshell (bash perche' sei in bash)
echo $MESSAGGIO → "ciao" ← eredita
echo $LOCALE → "" ← non eredita
CASO 3 — source (nessuna subshell)
─────────────────────────────────────────
shell corrente
│ LOCALE="solo qui" ← locale
│
source script.sh ← gira nella shell corrente
│ echo $LOCALE → "solo qui" ← stessa shell, vede tuttoLa regola non cambia con lo shebang — export serve sempre quando lanci uno script come processo separato. Solo source bypassa questa regola perche' non crea una subshell.
Cosa succede se l'interprete non esiste#
#!/bin/dash
echo "ciao"./script.sh
# -bash: ./script.sh: /bin/dash: bad interpreter: No such file or directoryNessun fallback — errore immediato. Il kernel cerca esattamente quel path.
Scenario Reale#
Uno script di backup gira perfettamente sul Mac dello sviluppatore con zsh ma fallisce sul server di produzione Ubuntu. Causa: nessuno shebang, e lo sviluppatore usa array bash-specific.
# Sul Mac (zsh) — funziona
./backup.sh
# Sul server (sh → dash) — fallisce
./backup.sh
# syntax error: unexpected "(" ← array bash-specific non riconosciutoSoluzione: aggiungere #!/bin/bash e verificare che bash sia installato sul server, oppure riscrivere in POSIX puro con #!/bin/sh.
Dove l'ho incontrato#
- Script Python
failed_logins.py— usa#!/usr/bin/env python3 - Script bash durante lo studio
Collegato a#
- system — categoria
- posix — lo standard che definisce il comportamento di sh
- shell-comparison — le diverse shell e le loro differenze
- export — serve sempre quando si lancia uno script come subshell
- shell-environment — variabili d'ambiente e loro ereditarieta'


