Est-il correct d'utiliser / bin / sh dans le hashbang si le shell Bourne n'est pas disponible dans une distribution?

15

En général, les scripts shell contiennent le commentaire suivant à la première ligne du fichier script: #!/bin/sh. Selon les recherches que j'ai faites, cela s'appelle "hash bang" et c'est un commentaire conventionnel. Ce commentaire informe Unix que ce fichier est exécuté par le Bourne Shell sous le répertoire /bin.

Ma question commence par ce point. Jusqu'à présent, je n'ai pas vu ce commentaire comme #!/bin/bash. C'est toujours #!/bin/sh. Cependant, les distributions Ubuntu n'ont pas le programme Bourne Shell. Ils ont le Bourne Again Shell (bash).

À ce stade, est-il correct de placer le commentaire #!/bin/shdans des scripts shell écrits dans des distributions Ubuntu?

Goktug
la source
1
Voir ma réponse à cette question de panne de serveur: #! / Bin / sh vs #! / Bin / bash pour une portabilité maximale .
Gordon Davisson
Plus communément appeléshebang
fpmurphy

Réponses:

16

#!/bin/shdevrait fonctionner sur toutes les distributions Unix et Unix. Il est généralement considéré comme le hashbang le plus portable tant que votre script est conforme à POSIX. Le /bin/shshell est censé être un shell qui implémente la norme shell POSIX, quel que soit le shell réel qui se fait passer pour le /bin/shshell.


#!/bin/shest normalement juste un lien maintenant car le shell Bourne n'est plus maintenu. Sur de nombreux systèmes Unix /bin/shsera un lien vers /bin/kshou /bin/ash, sur de nombreux systèmes Linux basés sur RHEL, ce sera un lien vers /bin/bash, cependant sur Ubuntu et sur de nombreux systèmes basés sur Debian, c'est un lien vers /bin/dash. Tous les shells, lorsqu'ils sont appelés en tant que sh, entreront en mode de compatibilité POSIX.

Le hashbang est un espace réservé important car il permet une portabilité beaucoup plus grande que les autres méthodes, tant que votre script est strictement conforme à POSIX (répété pour souligner l'importance).

Remarque: Quand bashest invoqué en mode POSIX, il autorisera toujours certaines choses non POSIX comme les [[tableaux et bien plus encore. Ces choses peuvent échouer sur un non- bashsystème.

Jesse_b
la source
3
"sur Ubuntu, c'est un lien vers / bin / dash" et en général d'autres systèmes basés sur Debian. L'IIRC sur FreeBSD / GhostBSD est également un lien symbolique /bin/ash.
Sergiy Kolodyazhnyy
Pour être entièrement portable, placez un espace entre le shebang et le chemin (absolu) de l'interpréteur. Certains systèmes recherchent au #! /lieu de simplement #!.
Simon Richter
5
@SimonRichter Une référence pour cela serait agréable à voir.
Kusalananda
@SergiyKolodyazhnyy La version installée de sh sur FreeBSD est ash et non un lien symbolique.
Rob
2
@Kusalananda, hmm, cette page dit que c'est un mythe, donc il peut probablement être ignoré.
Simon Richter
12

Vous l'avez en arrière. /bin/shn'est presque jamais une coquille Bourne de nos jours, et c'est quand c'est¹ que vous avez un problème lorsque vous utilisez une #! /bin/shshe-bang.

Le obus Bourne était un obus écrit à la fin des années 70 et a remplacé le précédent obus Thompson (également appelé sh). Au début des années 80, David Korn a écrit quelques extensions pour le shell Bourne, corrigé quelques bugs et conçu des maladresses (et en a introduit) et l'a appelé le shell Korn.

Au début des années 90, POSIX a spécifié le sh langage basé sur un sous-ensemble du shell Korn et la plupart des systèmes¹ ont désormais changé /bin/shpour le shell Korn ou un shell conforme à cette spécification. Dans le cas des BSD, ils ont progressivement changé leur /bin/sh, initialement (après qu'ils ne pouvaient plus utiliser le shell Bourne pour des raisons de licence) le shell Almquist, un clone du shell Bourne avec quelques extensions ksh donc il est devenu conforme POSIX.

Aujourd'hui, tous les systèmes POSIX ont un shell appelé sh(le plus souvent, mais pas nécessairement dans /bin, POSIX ne spécifie pas le chemin des utilitaires qu'il spécifie) qui est principalement conforme à POSIX. Il est généralement basé sur ksh88, ksh93, pdksh, bash, ash ou zsh², mais pas sur le shell Bourne car le shell Bourne n'a jamais été conforme POSIX³. Quelques - unes de ces coquilles ( bash, zsh, yashet certains pdkshdérivés permettent un mode compatible POSIX lorsqu'il est invoqué comme shet sont moins conformes par ailleurs).

bash(la réponse GNU au shell Korn) est en fait le seul shell open source (et on pourrait dire seulement actuellement maintenu car les autres sont généralement basés sur ksh88 qui n'a obtenu aucune nouvelle fonctionnalité depuis les années 90) qui a été certifié comme être conforme à POSIX sh(dans le cadre de la certification macOS).

Lorsque vous écrivez un script avec un #! /bin/sh -she-bang, vous devez utiliser une shsyntaxe standard (et également utiliser la syntaxe standard pour les utilitaires utilisés dans ce script si vous voulez être portable, ce n'est pas seulement le shell qui est impliqué lors de l'interprétation d'un shell script), peu importe l'implémentation de cet shinterpréteur de syntaxe standard utilisée ( ksh, bash...).

Peu importe que ces shells aient des extensions par rapport à la norme tant que vous ne les utilisez pas. C'est comme pour écrire du code C, tant que vous écrivez du code C standard et que vous n'utilisez pas d'extensions d'un compilateur (comme gcc) ou de l'autre, votre code devrait compiler OK quelle que soit l'implémentation du compilateur à condition que le compilateur soit conforme.

Ici, avec votre #! /bin/shelle-bang, votre principal problème serait les systèmes où /bin/shest le shell Bourne que par exemple ne prend pas en charge des fonctionnalités standard comme $((1+1)), $(cmd), ${var#pattern}... Dans ce cas , vous devrez peut - être contournements comme:

#! /bin/sh -
if false ^ true; then
  # in the Bourne shell, ^ is an alias for |, so false ^ true returns
  # true in the Bourne shell and the Bourne shell only.
  # Assume the system is Solaris 10 (older versions are no longer maintained)
  # You would need to adapt if you need to support some other system
  # where /bin/sh is the Bourne shell.
  # We also need to fix $PATH as the other utilities in /bin are
  # also ancient and not POSIX compatible.
  PATH=`/usr/xpg6/bin/getconf PATH`${PATH:+:}$PATH || exit
  export PATH
  exec /usr/xpg4/bin/sh "$0" "$@"
  # that should give us an environment that is mostly  compliant
  # to the Single UNIX Specification version 3 (POSIX.2004), the
  # latest supported by Solaris 10.
fi
# rest of the script

Par ailleurs, Ubuntu /bin/shn'est pas bashpar défaut. C'est de dashnos jours, un shell basé sur NetBSD sh, lui-même basé sur le shell Almquist qui est principalement compatible POSIX, sauf qu'il ne prend pas en charge les caractères multi-octets. Sur Ubuntu et d'autres systèmes basés sur Debian, vous pouvez choisir entre bashet dashpour /bin/shavec dpkg-reconfigure dash) 4 . shles scripts fournis avec Debian devraient fonctionner de la même manière dans les deux shells car ils sont écrits dans le standard de politique Debian (un surensemble du standard POSIX). Vous constaterez probablement qu'ils fonctionnent aussi bien dans zshl' shémulation de bosh( ou probablement pas ksh93et yashqui n'ont pas de fonction localintégrée (requis par la politique Debian mais pas POSIX)).

Tous les systèmes concernés par unix.stackexchange.com ont un POSIX sh quelque part. La plupart d'entre eux ont un /bin/sh(vous pouvez en trouver de très rares qui n'ont pas de /binrépertoire mais vous ne vous en souciez probablement pas), et c'est généralement un shinterpréteur POSIX (et dans de rares cas un shell Bourne (non standard) à la place ).

Mais shc'est le seul exécutable interpréteur de shell que vous pouvez être sûr de trouver sur un système. Pour les autres shells, vous pouvez être sûr que macOS, Cygwin et la plupart des distributions GNU / Linux auront bash. Les systèmes d'exploitation dérivés de SYSV (Solaris, AIX ...) auront généralement ksh88, éventuellement ksh93. OpenBSD, MirOS aura un dérivé pdksh. macOS aura zsh. Mais hors de cela, il n'y aura aucune garantie. Aucune garantie que ce soit bashou l'un de ces autres shells sera installé dans /binou ailleurs (il se trouve généralement dans /usr/local/binles BSD lors de l'installation par exemple). Et bien sûr aucune garantie de la version du shell qui sera installée.

Notez que ce #! /path/to/executablen'est pas une convention , c'est une fonctionnalité de tous les noyaux de type Unix ( introduite au début des années 80 par Dennis Ritchie ) qui permet d'exécuter des fichiers arbitraires en spécifiant le chemin de l'interpréteur dans une première ligne commençant par #!. Il peut s'agir de n'importe quel exécutable.

Lorsque vous exécutez un fichier dont la première ligne commence par #! /some/file some-optional-arg, le noyau finit par s'exécuter /some/fileavec some-optional-arg, le chemin du script et les arguments d'origine comme arguments. Vous pouvez faire cette première ligne #! /bin/echo testpour voir ce qui se passe:

$ ./myscript foo
test ./myscript foo

Lorsque vous utilisez à la /bin/sh -place de /bin/echo test, le noyau s'exécute /bin/sh - ./myscript foo, shinterprète le code de contenu stocké dans myscriptet ignore cette première ligne car il s'agit d'un commentaire (commence par #).


¹ Probablement le seul système aujourd'hui où l'un de nous rencontrera un /bin/shqui est basé sur le shell Bourne est Solaris 10. Solaris est l'un des rares Unices qui a décidé de garder un shell Bourne là pour la compatibilité descendante (le shlangage POSIX n'est pas complètement rétrocompatible avec le shell Bourne) et (au moins pour les déploiements de bureau et de serveur complet) ont le POSIX shailleurs (dans /usr/xpg4/bin/sh, basé sur ksh88), mais cela a changé dans Solaris 11 où /bin/shest maintenant ksh93. Les autres sont pour la plupart disparus.

² Le /bin/shMacOS / X était autrefoiszsh , mais plus tard changé en bash. Ce n'est pas zshl'objectif principal d'être utilisé comme une shimplémentation POSIX . Son shmode est avant tout de pouvoir embarquer ou appeler ( source) du shcode POSIX dans des zshscripts

³ Récemment, @schily a étendu le shell OpenSolaris (basé sur le shell SVR4, basé sur le shell Bourne) pour devenir compatible POSIX, appelé boshmais je ne suis pas au courant qu'il soit utilisé sur aucun système pour le moment. Avec ksh88ce fait un deuxième coque compatible POSIX basé sur le code de la coque Bourne

4 Dans les anciennes versions, vous pouviez également utiliser mkshou son lkshincarnation plus POSIX . C'est le shell MirOS (anciennement MirBSD) basé sur pdksh, lui-même basé sur le shell Forsyth (une autre réimplémentation du shell Bourne))

Stéphane Chazelas
la source
Petite chose (à propos de la réponse intermédiaire du code shell): Ne devriez-vous pas utiliser juste exec sh "$0" "$@"après avoir défini le PATH? shJ'aurais pensé que cela devrait être récupéré au bon endroit.
Kusalananda
1
@Kusalananda, cela devrait fonctionner mais est plus risqué car nous pourrions nous retrouver dans une boucle sans fin si quelque chose ne va pas (comme sh avec de mauvaises autorisations, getconf modifié ...). Quoi qu'il en soit, le reste est spécifique à Solaris 10, nous pourrions également coder en dur tout, y compris le contenu de $PATH.
Stéphane Chazelas
Toute citation pour OSX utilisant ksh comme shell POSIX. Son shell par défaut était tcsh mais je ne me souviens pas que bash n'était pas utilisé, d'autant plus que Korn Shell n'était pas open source avant la sortie d'OSX
user151019
1
@Mark, je n'ai pas dit qu'OSX avait déjà utilisé ksh. J'ai dit que c'était zsh et ils sont passés à bash (une version spéciale de bash).
Stéphane Chazelas
1
@fpmurphy, si vous regardez les premières versions, bash était déjà du côté de ksh partout où ksh n'était pas compatible avec le shell Bourne. Alors que vous avez dû attendre jusqu'à bash2 pour arriver au même niveau de fonctionnalité que ksh88, bash avait déjà déjà plusieurs extensions de ksh comme $(...), les modes emacs / vi, les extensions tilde / brace (celles initialement de csh), fc, typeset, alias , syntaxe des fonctions de style ksh ...
Stéphane Chazelas
8

Oui, vous pouvez l'utiliser #!/bin/shdans un script car il /bin/shest (espérons-le) prévu sur de tels systèmes, généralement via un lien quelconque qui se bashcomporte (plus ou moins) comme le shferait. Voici un système Centos7, par exemple, qui relie shà bash:

-bash-4.2$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Dec  4 16:48 /bin/sh -> bash
-bash-4.2$ 

Vous pouvez également l'utiliser #!/bin/bashsi vous écrivez un bashscript uniquement pour ce système et souhaitez utiliser des bashfonctionnalités. Cependant, ces scripts souffriront de problèmes de portabilité, par exemple sur OpenBSD où bashn'est installé que si l'administrateur prend la peine de l'installer (je ne le fais pas) et ensuite il est installé sur /usr/local/bin/bash, non /bin/bash. Un #!/bin/shscript strictement POSIX devrait être plus portable.

branler
la source
Notez ceci: "Lorsqu'il est appelé en tant que sh, Bash passe en mode POSIX après avoir lu les fichiers de démarrage." - gnu.org/software/bash/manual/bashref.html#Bash-POSIX-Mode
glenn jackman
2
Lorsque j'écris des scripts pour les serveurs RHEL dans mon travail, je les utilise #!/bin/bashprécisément pour pouvoir profiter des fonctionnalités non POSIX.
Monty Harder le
@MontyHarder IIRC sur RHEL, /bin/shc'est un lien symbolique vers bash, n'est-ce pas? Je sais que c'est sûr sur CentOS.
Sergiy Kolodyazhnyy
1
@SergiyKolodyazhnyy Oui, sur le serveur RHEL que je viens de vérifier, c'est un lien symbolique vers bash. Le binaire sait quel nom a été utilisé pour l'invoquer, et quand il s'appelle, shil agit comme de la vieille Bourne sh.
Monty plus dur
6

Tu as demandé

est-il correct de placer le commentaire #! / bin / sh dans des scripts shell écrits dans des distributions ubuntu?

La réponse dépend de ce que vous écrivez dans le script shell.

  • Si vous utilisez strictement des scripts portables compatibles POSIX et que vous n'utilisez aucune commande spécifique à bash, vous pouvez utiliser /bin/sh.

  • Si vous savez que vous ne jamais utiliser le script sur une machine avec bash, et que vous voulez utiliser la syntaxe spécifique à bash, alors vous devriez utiliser/bin/bash

  • Si vous voulez être sûr que le script fonctionne sur un assortiment de machines unix, vous devez utiliser uniquement la syntaxe POSIX conforme, et/bin/sh

  • Si vous utilisez régulièrement un autre shell (par exemple ksh , zsh ou tcsh ), et que vous souhaitez utiliser cette syntaxe dans votre script, vous devez utiliser l'interpréteur approprié (comme /bin/ksh93, /bin/zshou /bin/tcsh)

Stobor
la source
3

Le "#!" commentaire n'utilise pas toujours /bin/bashou /bin/sh. Il répertorie simplement ce que l'interprète doit être, pas seulement pour les scripts shell. Par exemple, mes scripts python commencent généralement par #!/usr/bin/env python.

Maintenant, la différence entre #!/bin/shet #!/bin/bashest que ce /bin/shn'est pas toujours un lien symbolique vers /bin/bash. Souvent mais pas toujours. Ubuntu est une exception notable ici. J'ai vu des scripts fonctionner correctement sur CentOS mais échouer sur Ubuntu car l'auteur a utilisé la syntaxe spécifique à bash avec #!/bin/sh.

aragaer
la source
1

Désolé de verser de l'eau froide sur toutes ces bonnes réponses qui disent que /bin/shc'est présent dans tous les systèmes Unix - il est présent, sauf dans le système Unix le plus utilisé de tous les temps: Android.

Android a son shell /system/bin/sh, et il n'y a généralement aucun moyen de créer un /bin/shlien, même sur un système enraciné (en raison de la façon dont le système est verrouillé à l'aide de selinux et des ensembles de limites de capacités (7)).

Pour ceux qui diront qu'Android n'est pas conforme à POSIX: la plupart des distributions Linux et BSD ne le sont pas non plus. Et l'existence de /bin/shce chemin n'est pas imposée par la norme :

Les applications doivent noter que la norme PATHpour le shell ne peut pas être supposée être /bin/shou /usr/bin/sh, et doit être déterminée par interrogation du PATHretourné par getconf PATH, en s'assurant que le chemin retourné est un chemin absolu et non un shell intégré.

mosvy
la source
Bien que tous les systèmes qui tentent d'être conformes à POSIX présentent de nombreux bogues de conformité (y compris ceux certifiés), Android (et la plupart des autres systèmes intégrés comme votre routeur ou votre système d'exploitation d'ampoule) n'ont pas l' intention d'être conformes à POSIX. Android est hors sujet ici, sauf en ce qui concerne son interface POSIX.
Stéphane Chazelas
@Christopher, lequel getconf? Par exemple, sous Solaris 11.4, est-ce que ce serait le cas /usr/bin? L'un dans /usr/xpg4/bin(pour la conformité SUSv2), celui dans /usr/xpg6/bin(pour la conformité SUSv3)? /usr/xpg7/bin(pour SUSv4)?
Stéphane Chazelas
@ StéphaneChazelas si nous voulons juger des intentions, je pense que je peux facilement trouver quelques citations de Linus Torvalds ou Theo de Raadt à l'effet qu'ils ne se soucient pas beaucoup de POSIX ;-) Et la plupart des systèmes embarqués sont basés sur linux + busybox, qui est à peine moins conforme à POSIX que le système typique de type Unix. Et a) Android n'est pas vraiment un système "embarqué" et b) un système Android pourrait être rendu compatible POSIX sans avoir un shell /bin/sh
mosvy
1
@mosvy, ce que vous dites est surtout vrai. Mais Torvalds ne peut parler que pour le noyau Linux. Chet Ramey se soucie de la conformité POSIX pour bash, RedHat se soucie de la conformité POSIX (ils parrainent le groupe Austin et s'assoient lors de leurs réunions de groupe, ils maintiennent une grande partie du logiciel GNU). L'idée est que POSIX est la seule norme que la plupart des systèmes de type Unix considèrent. Les utilisateurs se soucient de la compatibilité POSIX, car il est plus facile d'avoir des logiciels portables sur différents systèmes. Ce n'est pas idéal mais c'est mieux que rien. Android a sa propre API de programmation, le POSIX n'est pas vraiment un problème là-bas.
Stéphane Chazelas