Quand dois-je utiliser #! / Bin / bash et quand #! / Bin / sh?

104

Quand est-il #!/bin/bashplus approprié que #!/bin/shdans un script shell?

Hendré
la source
55
Lorsque vous utilisez des bashfonctions et une syntaxe plutôt que des shfonctions et une syntaxe.
Mokubai
3
Si cela aide quelqu'un, j’ai remarqué que vimcela bashmettrait en évidence des -ismes si votre script a le #!/bin/shshebang. Je ne le change que bashsi les choses deviennent trop poilues pour que les fonctionnalités bash soient nécessaires.
SeldomNeedy
@SeldomNeedy Certains éléments mis en évidence par défaut fonctionnent toutefois correctement dans n'importe quel shell POSIX. $(...)est particulièrement odieux. En outre, certains d'entre eux sont subtils [ <(...)et cmd >& filen'obtiennent aucune erreur en surbrillance, par exemple, ils n'ont tout simplement pas de surbrillance spéciale pour ce qu'ils veulent dire, avec ou sans g:is_bash]
Random832
@ Random832 On dirait qu'il y a eu une activité sur ce sujet récemment dans le suivi des problèmes de vim .
SeldomNeedy

Réponses:

150

En bref:

  • Plusieurs shells implémentent un sur-ensemble de la spécification POSIX sh . Sur différents systèmes, /bin/shun lien vers ash, bash, dash, ksh, zsh, etc. (Ce sera toujours compatible avec sh - jamais csh ni poisson.)

  • Tant que vous vous en tenez shuniquement aux fonctionnalités, vous pouvez (et même probablement le devriez) utiliser #!/bin/shet le script devrait fonctionner correctement, quel que soit son shell.

  • Si vous commencez à utiliser des fonctionnalités spécifiques à bash (par exemple, des tableaux), vous devez spécifiquement demander bash, car même s'il /bin/shappelle déjà bash sur votre système, il se peut que ce ne soit pas le cas du système de tous les autres et que votre script n'y soit pas exécuté. (Il en va de même pour zsh et ksh.) Vous pouvez utiliser shellcheck pour identifier les bashismes.

  • Même si le script est destiné uniquement à un usage personnel, vous remarquerez peut-être que certains systèmes d’exploitation changent /bin/shlors des mises à niveau. Par exemple, sous Debian, il s’agissait de bash, mais a été remplacé plus tard par un tiret très minimal. Des scripts qui utilisaient des bashismes mais qui s'étaient #!/bin/shsoudainement cassés.

Pourtant:

  • Même #!/bin/bashn'est pas très correct. Sur des systèmes différents, bash peut vivre dans /usr/binou /usr/pkg/binou /usr/local/bin.

  • Une option plus fiable consiste à #!/usr/bin/env bashutiliser $ PATH. (Bien que l' envoutil lui-même ne soit pas strictement garanti non plus, /usr/bin/envil fonctionne toujours sur plus de systèmes que lui /bin/bash.)

grawity
la source
10
Bonne réponse. Vouliez-vous dire que c'est CW?
Mokubai
3
Juste une remarque si bash est lancé à partir de /bin/shlà, il essaiera d’imiter les shfonctionnalités (voir la page de manuel ), ne sachant pas que des fonctionnalités spécifiques à Bash sont disponibles dans ce mode. envoutil est un outil posix qu'il devrait être trouvé dans la plupart des distributions, mais je ne suis pas sûr que certains puissent ne pas respecter posix.
Brice
8
Non, en fait, POSIX indique spécifiquement qu'il ne peut pas être supposé être dans /bin: " Les applications doivent noter que le PATH standard du shell ne peut pas être supposé être / bin / sh ou / usr / bin / sh, et doit être déterminé par interrogation du chemin PATH renvoyé par getconf PATH, en s'assurant que le chemin renvoyé est un chemin absolu et non un shell intégré ". Voir aussi cette question et plus précisément cette réponse .
Terdon
12
Hmm, eh bien, cela signifie donc que POSIX ne définit pas réellement un moyen portable de faire #!fonctionner les scripts?
grawity
4
POSIX sh n’est pas Bourne: c’est plus un dérivé du ksh précoce qu’un descendant direct de Bourne. Il est facile de les distinguer: echo foo ^ catémet foo ^ catdans POSIX sh et n’émet que foodans Bourne (comme ^un caractère de canal là-bas).
Charles Duffy
18

Utilisez le shebang correspondant au shell que vous avez réellement utilisé pour développer et déboguer votre script. Par exemple, si votre shell de connexion est bashet que vous exécutez votre script en tant qu'exécutable dans votre terminal, utilisez #!/bin/bash. Ne supposez pas simplement que, puisque vous n'avez pas utilisé de tableaux (ou de quelque bashfonction que ce soit dont vous avez connaissance), vous êtes sûr de choisir le shell que vous préférez. Il existe de nombreuses différences subtiles entre les shells ( echofonctions, boucles, etc.) qui ne peuvent être découvertes sans un test approprié.

Considérez ceci: si vous quittez #!/bin/bashet que vos utilisateurs ne l’ont pas, ils verront un message d’erreur clair, quelque chose comme:

Error: /bin/bash not found

La plupart des utilisateurs peuvent résoudre ce problème en moins d'une minute en installant le package approprié. D'autre part, si vous remplacez le shebang par #!/bin/shet le testez sur un système où se /bin/shtrouve un lien symbolique vers /bin/bash, vos utilisateurs qui n'en ont pas bashauront des problèmes. Ils verront probablement un message d'erreur cryptique du type:

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

Cela peut prendre des heures à résoudre, et il n'y aura aucune idée de quel shell ils auraient dû utiliser.

Il n'y a pas beaucoup de raisons pour lesquelles vous voudriez utiliser un shell différent dans le shebang. Une des raisons est que le shell que vous avez utilisé n'est pas répandu. Une autre solution consiste à obtenir des performances shbeaucoup plus rapides sur certains systèmes, ET votre script constituera un goulot d'étranglement en termes de performances. Dans ce cas, testez minutieusement votre script avec le shell cible, puis modifiez le shebang.

Dmitry Grigoryev
la source
@Hastur Je ne comprends pas votre commentaire. Le shebang va certainement définir quel shell sera utilisé pour exécuter le script, pensez-vous que ma réponse implique le contraire?
Dmitry Grigoryev
1
Oublie. J'ai mal compris la partie "pourquoi tu voudrais utiliser" ...
Hastur
6

Vous ne devriez utiliser que jamais #! /bin/sh.

Vous ne devriez jamais utiliser d'extensions bash (ou zsh, ou fish, ou ...) dans un script shell.

Vous ne devriez jamais écrire que des scripts shell qui fonctionnent avec n'importe quelle implémentation du langage shell (y compris tous les programmes "utilitaires" associés au shell lui-même). De nos jours, vous pouvez probablement prendre POSIX.1-2001 (et non -2008) comme autorité pour tout ce que le shell et les utilitaires sont capables de faire, mais sachez que vous serez peut-être appelé un jour à porter votre script sur un système hérité (par exemple, Solaris ou AIX) dont la coque et les utilitaires ont été gelés vers 1992.

Quoi, sérieusement?!

Oui, vraiment.

Voici la chose: Shell est un langage de programmation terrible . La seule chose qui lui reste à faire, c’est /bin/shle seul et unique interprète de script que chaque installation Unix possède.

Voici l’autre chose: une certaine itération de l’interpréteur ( /usr/bin/perl) Perl 5 principal est plus susceptible d’être disponible sur une installation Unix sélectionnée de manière aléatoire qu’elle ne l’ (/(usr|opt)(/(local|sfw|pkg)?)?/bin/bashest. D'autres bons langages de script (Python, Ruby, node.js, etc. - Je vais même inclure PHP et Tcl dans cette catégorie lors de la comparaison avec un shell) sont aussi à peu près aussi disponibles que bash et d'autres shells étendus.

Par conséquent, si vous avez la possibilité d'écrire un script bash, vous avez la possibilité d'utiliser un langage de programmation qui n'est pas terrible, à la place.

Maintenant, de simples scripts shell, du genre qui ne fait que lancer quelques programmes dans l'ordre d'un job cron ou autre, rien de mal à les laisser en tant que scripts shell. Mais les scripts shell simples n'ont pas besoin de tableaux, de fonctions ou [[même. Et vous ne devriez écrire des scripts shell compliqués que lorsque vous n'avez pas d'autre choix. Les scripts Autoconf, par exemple, sont toujours des scripts shell. Mais ces scripts doivent être exécutés à chaque incarnation /bin/shcorrespondant au programme en cours de configuration. et cela signifie qu'ils ne peuvent utiliser aucune extension. Vous n'avez probablement pas à vous soucier des anciens Unix propriétaires, mais vous devriez probablement vous soucier des BSD Open Source actuels, dont certains ne sont pas installésbashpar défaut, et les environnements intégrés qui ne vous donnent qu'un shell minimal et busybox.

En conclusion, au moment où vous vous trouvez à vouloir une fonctionnalité qui n’est pas disponible dans le langage shell portable, c’est le signe que le script est devenu trop compliqué pour rester un script shell. Réécris-le plutôt dans une meilleure langue.

zwol
la source
4
Désolé mais c'est un peu bête. Oui, si vous écrivez quelque chose qui sera i) distribué et ii) dans différents environnements, vous devriez vous en tenir à la base sh. C’est cependant loin de dire qu’il ne faut jamais utiliser d’autres coquillages. Il y a des milliers (probablement beaucoup plus) de scripts écrits chaque jour et la grande majorité d'entre eux ne fonctionneront jamais que sur une seule machine. Vos conseils ont du sens pour le code de production, mais pas pour les tâches "sysdaminy" quotidiennes pour lesquelles la plupart des scripts sont écrits. Si je sais que mon script ne sera utilisé que par moi-même et sur une machine Linux, bash convient.
Terdon
1
@terdon Le fait est que si vous avez la possibilité d'écrire un script bash, vous avez également la possibilité d'écrire un script perl (ou autre), et c'est toujours le meilleur choix.
dimanche
@terdon La réponse n'est sûrement pas idiote, votre commentaire est simplement naïf. Les scripts shell sont difficiles à déboguer par rapport à Perl, pour lesquels on peut facilement utiliser des IDE complets avec un débogage graphique et tout. En outre, la plupart des scripts écrits chaque jour pour une petite chose à faire ont tendance à être changés, à grandir, à être agrandis, sont copiés à titre d'exemples pour des collègues ou le réseau, etc. Il n'y a probablement aucune raison de prendre un tel risque.
Thorsten Schöning
@ ThorstenSchöning j'ai dit un peu idiot. Si c'était Unix & Linux ou même Stack Overflow, je pourrais même être d'accord. Si nous parlons de bonnes pratiques générales ou de programmeurs professionnels, il est bien sûr préférable de s'en tenir à la base POSIX sh. Cependant, il s’agit de Super User , un site destiné aux utilisateurs finaux et demandant aux utilisateurs de ne jamais utiliser bash ou zsh lorsqu’ils écrivent des scripts shell est, à mon avis, extrême.
Terdon
@terdon Il est en réalité plus important, à mon avis, de décourager les utilisateurs finaux d'écrire des scripts shell complexes. Les professionnels ont en moyenne un niveau de compétences plus élevé (ils ont donc plus de chances de pouvoir faire face aux pièges d'un script shell complexe), doivent savoir avec une certaine précision les environnements dans lesquels ils doivent être portables et sont payés pour ne pas abandonner dans la frustration.
dimanche
4

Généralement, si le temps est plus important que la fonctionnalité, vous utiliserez le shell le plus rapide. sh est souvent associé à dash et a tendance à être utilisé pour les tâches cron de root ou les opérations de traitement par lots où chaque (nano) seconde compte.

Mckenzm
la source
2
Le chargement d'un interpréteur de shell supplémentaire à partir du système de fichiers peut annuler un tel avantage. Tout ce qui compte des nanosecondes (du même ordre de grandeur qu'un cycle machine réel) est le domaine des programmeurs en langage C et en assembleur.
rackandboneman
Les nanosecondes ne font une différence dans les tâches cron que si vous en avez des milliards, et cron ne pourra pas en gérer autant.
Dmitry Grigoryev
1
Même si mckenzm a un peu exagéré, force est de constater qu’avoir plus de performances, c’est mieux! Ne vous attardez pas sur la partie "nano". (Les nanosecondes ne comptent même pas en C, sauf si c'est une boucle interne sur un système en temps réel ou autre!)
jpaugh
1
@jpaugh Sauf si vous travaillez avec un système (hérité) qui suppose que certaines opérations prendront au moins une période de temps spécifique. Ça arrive!
JAB
3

Pour être encore plus bref, utilisez shsi la portabilité sur la plupart des systèmes est primordiale et bashsi vous souhaitez utiliser certaines de ses fonctionnalités spécifiques, telles que les tableaux, si la version de bash le prend en charge.

Spencer Williams
la source
1
  • sh (dans la plupart des cas), bash si requis (ou ksh, ou autre)

Le chemin le plus sûr. Quand cela est codé en dur, cela devrait être / usr / bin / shellOfChoicemais une nouvelle convention que j'essaie d'utiliser toujours maintenant - comme 'emplacements par défaut' via une modification, PATH peut changer:

#! / usr / bin / env sh ou
#! / usr / bin / env bash
ou pour, par exemple, les scripts Perl
#! / usr / bin / env perl -w

Bien sûr, lorsque vous avez une raison pour qu'un script prenne JAMAIS automatiquement un nouveau PATH, continuez à le coder en dur - et ensuite, / usr / bin / quelque chose devrait être le chemin le plus probable.

Michael Felt
la source