Quando devo usare #! / Bin / bash e quando #! / Bin / sh?


104

Quando è #!/bin/bashpiù appropriato che #!/bin/shin uno script di shell?


55
Quando si utilizzano bashfunzioni e sintassi anziché shfunzioni e sintassi.
Mokubai


3
Se aiuta qualcuno, ho notato che vimmetterà in evidenza bash-ismi se la tua sceneggiatura ha lo #!/bin/shshebang. Lo cambio solo bashse le cose diventano abbastanza pelose per iniziare a necessitare di funzionalità bash.
SeldomNeedy,

@SeldomNeedy Alcune delle cose che evidenzia per impostazione predefinita funzionano bene in qualsiasi shell POSIX. $(...)è particolarmente odioso. Inoltre, alcuni di essi sono sottili [ <(...)e cmd >& filenon ricevono alcun errore di evidenziazione, ad esempio, semplicemente non hanno un evidenziazione speciale per ciò che significano, con o senza g:is_bash]
Casuale 832

@ Random832 Sembra che ci sia stata qualche attività su questo argomento recentemente nel tracker dei problemi di Vim .
SeldomNeedy,

Risposte:


150

In breve:

  • Esistono diverse shell che implementano un superset della specifica sh POSIX . Su sistemi diversi, /bin/shpotrebbe esserci un collegamento a ash, bash, dash, ksh, zsh e c. (Sarà sempre sh-compatibile però - mai csh o pesce.)

  • Finché ti attieni shsolo alle funzionalità, puoi (e probabilmente anche dovresti) usare #!/bin/she lo script dovrebbe funzionare bene, indipendentemente dalla shell.

  • Se inizi a utilizzare funzionalità specifiche di bash (ad es. Array), dovresti richiedere specificamente bash, perché, anche se /bin/shinvoca già bash sul tuo sistema, potrebbe non essere sul sistema di tutti gli altri e il tuo script non verrà eseguito lì. (Lo stesso vale ovviamente per zsh e ksh.) È possibile utilizzare il controllo shell per identificare i bashismi.

  • Anche se lo script è solo per uso personale, potresti notare che alcuni sistemi operativi cambiano /bin/shdurante gli aggiornamenti - ad esempio su Debian era usato per essere bash, ma in seguito è stato sostituito con un trattino minimo. Script che utilizzavano basismi ma che si erano #!/bin/shimprovvisamente interrotti.

Però:

  • Anche #!/bin/bashnon è molto corretto. Su sistemi diversi, bash potrebbe vivere in /usr/bino /usr/pkg/bino /usr/local/bin.

  • Un'opzione più affidabile è #!/usr/bin/env bash, che utilizza $ PATH. (Anche se lo envstrumento stesso non è strettamente garantito, /usr/bin/envfunziona ancora su più sistemi di quanti ne /bin/bashfaccia.)


10
Bella risposta. Intendevi renderlo in CW?
Mokubai

3
Solo un'osservazione se bash viene eseguito da /bin/shallora proverà a imitare le shfunzionalità (vedi la pagina man ), non sono sicuro che funzioni specifiche di bash siano disponibili in questa modalità. envLo strumento è uno strumento posix che dovrebbe essere trovato nella maggior parte della distribuzione, ma che non sono sicuro poiché alcuni potrebbero non rispettare posix.
Brice,

8
No, in effetti POSIX afferma specificamente che non si può presumere che si trovi in /bin: "Le applicazioni dovrebbero notare che il PERCORSO standard per la shell non può essere assunto come / bin / sh o / usr / bin / sh e dovrebbe essere determinato interrogando il PERCORSO restituito da getconf PERCORSO, assicurandosi che il nome di ritorno restituito sia un nome di percorso assoluto e non una shell incorporata ". Vedi anche questa domanda e, in particolare, questa risposta .
terdon,

12
Hmm, beh, quindi ciò significa che POSIX in realtà non definisce un modo portatile per far #!funzionare gli script?
Grawity,

4
POSIX sh non è Bourne: è più un derivato dei primi ksh che un discendente diretto di Bourne. Distinguerli è facile: echo foo ^ catemette foo ^ catin POSIX sh, ed emette solo fooin Bourne (com'è ^un personaggio pipe lì).
Charles Duffy,

18

Usa lo shebang corrispondente alla shell che hai effettivamente usato per sviluppare ed eseguire il debug del tuo script. Vale a dire se la shell di accesso è bash, e si esegue lo script come eseguibile nel terminale, utilizzare #!/bin/bash. Non dare per scontato che dal momento che non hai utilizzato array (o qualsiasi bashfunzione di cui sei a conoscenza), sei sicuro di scegliere qualsiasi shell che ti piace. Ci sono molte sottili differenze tra le shell ( echo, funzioni, loop, tu lo chiami) che non possono essere scoperte senza test adeguati.

Considera questo: se te ne vai #!/bin/bashe i tuoi utenti non ce l'hanno, vedranno un chiaro messaggio di errore, qualcosa del genere

Error: /bin/bash not found

La maggior parte degli utenti può risolvere questo problema in meno di un minuto installando il pacchetto appropriato. D'altra parte, se si sostituisce lo shebang #!/bin/she lo si verifica su un sistema in cui /bin/shè presente un collegamento simbolico /bin/bash, gli utenti che non lo hanno bashavranno problemi. Molto probabilmente vedranno un messaggio di errore criptico come:

Error in script.sh line 123: error parsing token xyz

Questo potrebbe richiedere ore per essere risolto, e non ci saranno indizi su quale shell avrebbero dovuto usare.

Non ci sono molte ragioni per cui vorresti usare una shell diversa nello shebang. Uno dei motivi è quando la shell che hai usato non è molto diffusa. Un altro è quello di ottenere prestazioni shche sono significativamente più veloci su alcuni sistemi, E il tuo script sarà un collo di bottiglia delle prestazioni. In tal caso, prova accuratamente il tuo script con la shell di destinazione, quindi modifica lo shebang.


@Hastur Non ricevo il tuo commento. Lo shebang definirà sicuramente quale shell verrà utilizzata per eseguire lo script, pensi che la mia risposta implichi diversamente?
Dmitry Grigoryev,

1
Dimenticalo. Ho frainteso la parte "perché dovresti usare" ...
Hastur,

6

Si dovrebbe usare sempre e solo #! /bin/sh.

Non utilizzare mai le estensioni bash (o zsh, o fish, o ...) in uno script di shell, mai.

Dovresti sempre scrivere script di shell che funzionino con qualsiasi implementazione del linguaggio di shell (inclusi tutti i programmi di "utilità" che vanno insieme alla shell stessa). In questi giorni puoi probabilmente prendere POSIX.1-2001 ( non -2008) come autorevole per ciò che la shell e le utility sono in grado di fare, ma tieni presente che un giorno potresti essere chiamato a portare il tuo script su un sistema legacy (ad esempio Solaris o AIX) la cui shell e utility sono state congelate intorno al 1992.

Cosa, sul serio ?!

Si, seriamente.

Ecco il punto: Shell è un terribile linguaggio di programmazione. L'unica cosa che deve fare è che /bin/shè l'unico e unico interprete di script che ogni installazione di Unix è garantita.

Ecco l'altra cosa: alcune iterazioni dell'interprete core Perl 5 ( /usr/bin/perl) hanno più probabilità di essere disponibili su un'installazione Unix selezionata casualmente di quanto non lo (/(usr|opt)(/(local|sfw|pkg)?)?/bin/bashsia. Altri buoni linguaggi di scripting (Python, Ruby, node.js, ecc. - Includerò anche PHP e Tcl in quella categoria quando si confronta con la shell) sono approssimativamente disponibili come bash e altre shell estese.

Pertanto, se hai la possibilità di scrivere uno script bash, hai la possibilità di usare un linguaggio di programmazione che non è terribile, invece.

Ora, semplici script di shell, il tipo che esegue solo alcuni programmi in una sequenza da un lavoro cron o qualcosa del genere, non c'è nulla di sbagliato nel lasciarli come script di shell. Ma semplici script di shell non hanno bisogno di matrici o funzioni o [[addirittura. E dovresti scrivere script di shell complicati solo quando non hai altra scelta. Gli script di autoconf, ad esempio, sono correttamente script di shell. Ma quegli script devono essere eseguiti su ogni incarnazione /bin/shrilevante per il programma che si sta configurando. e ciò significa che non possono utilizzare alcuna estensione. Oggigiorno probabilmente non devi preoccuparti dei vecchi Unix proprietari, ma probabilmente dovresti preoccuparti degli attuali BSD open source, alcuni dei quali non si installanobashper impostazione predefinita, e ambienti incorporati che offrono solo una shell minima e busybox.

In conclusione, nel momento in cui ti ritrovi a desiderare una funzione che non è disponibile nel linguaggio della shell portatile, questo è un segno che lo script è diventato troppo complicato per rimanere uno script di shell. Riscriverlo invece in una lingua migliore.


4
Scusa ma è un po 'sciocco. Sì, se stai scrivendo qualcosa che sarà i) distribuito e ii) in ambienti diversi, dovresti attenersi a quelli di base sh. Questo, tuttavia, è ben lungi dall'affermare che non si dovrebbero mai usare altri gusci. Ci sono migliaia (probabilmente molti più) di script scritti ogni giorno e la stragrande maggioranza di questi verrà eseguita su una sola macchina. Il tuo consiglio ha senso sul codice di produzione, ma non per i lavori quotidiani "sysdaminy" per cui sono scritti la maggior parte degli script. Se so che il mio script verrà usato solo da me e su una macchina Linux, bash va bene.
terdon,

1
@terdon Il punto è che se hai la possibilità di scrivere uno script bash, hai anche la possibilità di scrivere uno script perl (o qualunque altra cosa), e questa è sempre la scelta migliore.
zwol,

@terdon La risposta non è certo sciocca, il tuo commento invece è semplicemente ingenuo. Gli script della shell sono terribili da debug rispetto a Perl e simili, per i quali si possono facilmente usare IDE completi con debug grafico e tutto il resto. Inoltre, la maggior parte degli script scritti ogni giorno per alcune piccole cose da fare tendono a essere cambiati e modificati di nuovo, crescere, essere copiati come esempi per i colleghi o la rete, ecc. Probabilmente non c'è motivo di correre un rischio simile.
Thorsten Schöning,

@ ThorstenSchöning ho detto un po ' sciocco. Se questo fosse Unix & Linux o addirittura Stack Overflow, potrei anche essere d'accordo. Se stiamo parlando di buone pratiche generali o di programmatori professionisti, ovviamente è meglio attenersi ai POSIX di base sh. Tuttavia, questo è Super User , un sito rivolto agli utenti finali e che dice alle persone di non usare mai bash o zsh quando scrivere script di shell è, secondo me, estremo.
terdon,

@terdon In realtà è più importante, secondo me, scoraggiare gli utenti finali dalla scrittura di script shell complessi. I professionisti hanno in media un livello di abilità più elevato (quindi hanno maggiori probabilità di essere in grado di far fronte alle insidie ​​dei complessi script di shell), dovrebbero sapere con una certa precisione a quali ambienti devono essere portatili e vengono pagati per non arrendersi nella frustrazione.
zwol,

4

Generalmente se il tempo è più importante della funzionalità, utilizzerai la shell più veloce. sh è spesso aliasato con trattino e tende ad essere usato per le attività cron o le operazioni batch di root dove ogni secondo (nano) conta.


2
Il caricamento di un interprete di shell aggiuntivo dal filesystem può negare tale vantaggio e tutto ciò in cui i nanosecondi (che sono nello stesso ordine di grandezza di un ciclo macchina effettivo) contano è il dominio dei programmatori di linguaggio C e assembly.
Rackandboneman,

I nanosecondi fanno la differenza nei lavori cron solo se ne hai miliardi, e cron non sarà comunque in grado di gestirne così tanti.
Dmitry Grigoryev l'

1
Anche se mckenzm ha esagerato un po ', non si può negare che avere più prestazioni sia meglio! Non rimanere bloccato sulla parte "nano". (I nanosecondi non contano nemmeno in C a meno che non sia un loop interno su un sistema in tempo reale o simile!)
jpaugh,

1
@jpaugh A meno che tu non stia lavorando con un sistema (legacy) che presuppone che determinate operazioni richiedano almeno un determinato periodo di tempo. Succede!
JAB,

3

Per essere ancora più brevi, utilizzare shse la portabilità nella maggior parte dei sistemi è la cosa più importante e bashse si desidera utilizzare alcune delle sue caratteristiche specifiche, come gli array, se la versione di bash lo supporta.


1
  • sh (per la maggior parte dei casi), bash se specificamente richiesto (o ksh, o qualsiasi altra cosa)

Il percorso più sicuro. una volta codificato, sarebbe / usr / bin / shellOfChoicema una nuova convenzione che sto cercando di usare sempre ora - poiché le 'posizioni predefinite' tramite una modifica che PATH può cambiare è:

#! / usr / bin / env sh o
#! / usr / bin / env bash
o, ad esempio, script perl
#! / usr / bin / env perl -w

Ovviamente, quando hai una ragione per cui uno script NON prende MAI automaticamente un nuovo PERCORSO, allora continua ad avere un codice hard - e quindi / usr / bin / qualcosa dovrebbe essere il percorso più probabile da usare.

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.