web-dev-qa-db-fra.com

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

Généralement, les scripts Shell contiennent le commentaire suivant à la première ligne du fichier de 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/sh dans des scripts Shell écrits dans des distributions Ubuntu?

15
Goktug

#!/bin/sh devrait 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/sh Shell est censé être un Shell qui implémente la norme POSIX Shell, quel que soit le Shell réel qui se fait passer pour le /bin/sh Coquille.


#!/bin/sh n'est normalement plus qu'un lien maintenant car le Bourne Shell n'est plus maintenu. Sur de nombreux systèmes Unix /bin/sh sera un lien vers /bin/ksh ou /bin/ash, sur de nombreux systèmes Linux basés sur RHEL, ce sera un lien vers /bin/bash, cependant sur Ubuntu et 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: Lorsque bash est invoqué en mode POSIX, il autorise toujours certaines choses non POSIX comme [[, tableaux et plus encore. Ces choses peuvent échouer sur un système nonbash.

16
jesse_b

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

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

Au début des années 90, POSIX a spécifié la langue sh basée sur un sous-ensemble du Korn Shell et la plupart des systèmes¹ ont maintenant changé leur /bin/sh Vers le Korn Shell 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 Bourne Shell pour des raisons de licence) l'Almquist Shell, un clone du Bourne Shell avec quelques extensions ksh, il est donc devenu POSIX conforme.

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 Bourne Shell car le Bourne Shell n'a jamais été conforme à POSIX³. Quelques-uns de ces shells (bash, zsh, yash et certains dérivés pdksh activent un mode compatible POSIX lorsqu'ils sont appelés en tant que sh et sont moins conformes sinon).

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

Lorsque vous écrivez un script avec une transgression #! /bin/sh -, Vous devez utiliser une syntaxe standard sh (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 script Shell), peu importe quelle implémentation de cet interpréteur de syntaxe sh standard est utilisé (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 l'écriture de code C, tant que vous écrivez du code C standard et 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 ce compilateur est conforme.

Ici, avec votre coup de #! /bin/sh, Votre problème principal serait les systèmes où /bin/sh Est le Bourne Shell qui, par exemple, ne prend pas en charge les fonctionnalités standard comme $((1+1)), $(cmd), ${var#pattern} ... Dans ce cas, vous aurez peut-être besoin de solutions 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, /bin/sh D'Ubuntu n'est pas bash par défaut. Il s'agit de dash de nos 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 bash et dash pour /bin/sh Avec dpkg-reconfigure dash)4. Les scripts sh 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 sur-ensemble du standard POSIX). Vous constaterez probablement qu'ils fonctionnent également bien dans l'émulation zsh de sh ou bosh (probablement pas ksh93 Ni yash qui ne 'ai pas de code intégré local (requis par la politique Debian mais pas POSIX)).

Tous les systèmes à portée sur 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 répertoire /bin Mais vous ne vous en souciez probablement pas), et c'est généralement un POSIX sh interprète (et dans de rares cas un Bourne Shell (non standard) à la place).

Mais sh est le seul exécutable interpréteur 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 de savoir si bash ou l'un de ces autres shells sera installé dans /bin Ou ailleurs (il se trouve généralement dans /usr/local/bin Sur les BSD lorsqu'ils sont installés par exemple). Et bien sûr, aucune garantie de la version du Shell qui sera installée.

Notez que le #! /path/to/executable N'est pas une convention , c'est une fonctionnalité de tous les noyaux de type Unix ( introduit au début 80s 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 exécuter /some/file Avec some-optional-arg, Le chemin du script et les arguments d'origine comme arguments. Vous pouvez faire cette première ligne #! /bin/echo test Pour voir ce qui se passe:

$ ./myscript foo
test ./myscript foo

Lorsque vous utilisez /bin/sh - Au lieu de /bin/echo test, Le noyau exécute /bin/sh - ./myscript foo, sh interprète le code de contenu stocké dans myscript et l'ignore en premier car il s'agit d'un commentaire (commence par #).


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

² Le /bin/sh De MacOS/X était auparavant zsh , mais plus tard changé en bash. Ce n'est pas l'objectif principal de zsh d'être utilisé en tant qu'implémentation POSIX sh. Son mode sh consiste principalement à pouvoir incorporer ou appeler (source) du code POSIX sh dans des scripts zsh

³ Récemment, @ schily a étendu OpenSolaris Shell (basé sur le SVR4 Shell, basé sur le Bourne Shell) pour devenir conforme à POSIX, appelé bosh mais je Je ne sais pas encore qu'il est utilisé sur n'importe quel système. Avec ksh88, Cela en fait un deuxième shell compatible POSIX basé sur le code du Bourne Shell

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

12
Stéphane Chazelas

Oui, vous pouvez utiliser #!/bin/sh dans un script car /bin/sh est (espérons-le) prévu sur de tels systèmes, généralement via un lien quelconque qui fait que bash se comporte (plus ou moins) comme le ferait un sh. 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 utiliser #!/bin/bash si vous écrivez un script bash uniquement pour ce système et que vous souhaitez utiliser les fonctionnalités bash. Cependant, de tels scripts souffriront de problèmes de portabilité, par exemple sur OpenBSD où bash n'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, ne pas /bin/bash. Un strictement POSIX #!/bin/sh le script devrait être plus portable.

8
thrig

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, alors vous pouvez utilisation /bin/sh.

  • Si vous savez que vous n'utilisez le script que sur une machine avec bash et que vous souhaitez utiliser une syntaxe spécifique à bash, alors vous devrait utilisez /bin/bash

  • Si vous voulez être sûr que le script fonctionnera sur un assortiment de machines Unix, alors vous devrait utilisez uniquement une syntaxe compatible POSIX et /bin/sh

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

6
Stobor

Le "#!" le commentaire n'utilise pas toujours /bin/bash ou /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/sh et #!/bin/bash est-ce /bin/sh n'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.

3
aragaer

Désolé d'avoir versé de l'eau froide sur toutes ces bonnes réponses qui disent que /bin/sh 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 dans /system/bin/sh, et il n'y a généralement aucun moyen de faire un /bin/sh link, même sur un système enraciné (en raison de la façon dont le système est verrouillé par l'utilisation de selinux et des ensembles de limites de capacités (7)).

Pour ceux qui diront que Android n'est pas compatible POSIX: la plupart des distributions Linux et BSD ne le sont pas non plus. Et l'existence de /bin/sh avec ce chemin n'est pas mandaté par le standard :

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 PATH retourné par getconf PATH, garantissant que le chemin d'accès renvoyé est un chemin d'accès absolu et non un shell intégré.

1
mosvy