#! / bin / sh vs #! / bin / bash pour une portabilité maximale

cherouvim 09/08/2017. 3 answers, 2.834 views
linux bash shell sh

Je travaille habituellement avec les serveurs Ubuntu LTS, à partir de ce que j'ai compris symlink /bin/sh /bin/dash . Beaucoup d'autres distros par symlink /bin/sh to /bin/bash .

À partir de cela, je comprends que si un script utilise #!/bin/sh en haut, il est possible qu'il ne fonctionne pas de la même manière sur tous les serveurs?

Existe-t-il une pratique suggérée sur quel shell utiliser pour les scripts lorsque l'on veut la portabilité maximale de ces scripts entre les serveurs?

1 Comments
Thorbjørn Ravn Andersen 08/01/2017
Il existe de légères différences entre les différentes coquilles. Si la portabilité est la plus importante pour vous, utilisez #!/bin/sh et n'utilisez pas autre chose que ce que le shell original a fourni.

3 Answers


Gordon Davisson 08/01/2017.

Il existe environ quatre niveaux de portabilité pour les scripts shell (en ce qui concerne la ligne shebang):

  1. Le plus portatif: utilisez un #!/bin/sh shebang et n'utilisez only la syntaxe de shell de base spécifiée dans la norme POSIX . Cela devrait fonctionner à peu près dans n'importe quel système POSIX / unix / linux. (Eh bien, à l'exception de Solaris 10 et plus tôt, qui possédait le véritable coquille Bourne, antérieur à POSIX tellement non conforme, comme /bin/sh .)

  2. Deuxième plus portable: utilisez une ligne shebang #!/bin/bash (ou #!/usr/bin/env bash ) et respectez les fonctionnalités bash v3. Cela fonctionnera sur tout système ayant bash (dans l'emplacement prévu).

  3. Troisième plus portable: utilisez une ligne shebang #!/bin/bash (ou #!/usr/bin/env bash ) et utilisez les fonctionnalités bash v4. Cela échouera sur tout système qui possède bash v3 (p. Ex. MacOS, qui doit l'utiliser pour des raisons de licence).

  4. Moins portable: utilisez un #!/bin/sh shebang et utilisez les extensions bash sur la syntaxe POSIX shell. Cela échouera sur tout système qui a autre chose que bash pour / bin / sh (comme les versions récentes d'Ubuntu). Ne jamais faire cela; Ce n'est pas seulement un problème de compatibilité, mais c'est tout simplement faux. Malheureusement, c'est une erreur que beaucoup de gens font.

Ma recommandation: utilisez le plus conservateur des trois premiers qui fournit toutes les fonctionnalités de shell dont vous avez besoin pour le script. Pour une portabilité maximale, utilisez l'option n ° 1, mais dans mon expérience, certaines fonctionnalités bash (comme les tableaux) sont assez utiles pour aller avec # 2.

La pire chose que vous pouvez faire est # 4, en utilisant le faux shebang. Si vous ne savez pas quelles fonctionnalités sont POSIX de base et qui sont des extensions bash, soit collagez avec un bash shebang (c.-à-d. L'option n ° 2), soit testez le script à fond avec un shell très basique (comme un tiret sur vos serveurs Ubuntu LTS). Le wiki Ubuntu contient une bonne liste de bashismes à surveiller .

Il existe de très bonnes informations sur l'histoire et les différences entre les shells dans la question Unix & Linux: «Qu'est-ce que cela signifie être compatible?» et la question Stackoverflow "Différence entre sh et bash" .

Aussi, sachez que la coque n'est pas la seule chose qui diffère entre les différents systèmes; Si vous avez l'habitude de linux, vous êtes habitué aux commandes GNU, qui ont beaucoup d'extensions non standard que vous ne trouverez peut-être pas sur d'autres systèmes unix (p. ex. bsd, macos). Malheureusement, il n'y a pas de règle simple ici, il suffit de connaître la gamme de variation pour les commandes que vous utilisez.

L'une des commandes les plus grossières en termes de portabilité est l'une des plus basiques: echo . Chaque fois que vous l'utilisez avec toutes les options (p. Ex. echo -n ou echo -e ), ou avec des évasions (barres obliques) dans la chaîne à imprimer, différentes versions feront différentes choses. Chaque fois que vous souhaitez imprimer une chaîne sans fil de ligne après, ou avec des échappements dans la chaîne, utilisez plutôt printf (et apprenez comment cela fonctionne - c'est plus compliqué que echo est). La commande ps est également un gâchis .

Une autre chose générale à surveiller est des extensions récentes / GNUish pour la syntaxe de l'option de commande: le format de commande ancien (standard) est que la commande est suivie d'options (avec un seul tiret, et chaque option est une seule lettre), suivie de arguments de commande. Les variantes récentes (et souvent non portables) incluent des options longues (généralement introduites avec -- ), permettant aux options de venir après les arguments et d'utiliser -- pour séparer les options des arguments.

5 comments
12 pabouk 07/30/2017
La quatrième option est simplement une mauvaise idée. Ne l'utilisez pas.
3 Gordon Davisson 07/30/2017
@pabouk Je suis tout à fait d'accord, alors j'ai édité ma réponse pour clarifier.
1 jlliagre 07/30/2017
Votre première déclaration est légèrement trompeuse. La norme POSIX ne spécifie rien sur le shebang en dehors de l'utilisation, ce qui conduit à un comportement non spécifié. De plus, POSIX ne précise pas où le shell posix doit être situé, seul son nom ( sh ), donc /bin/sh ne garantit pas le bon chemin. Le plus portable est alors de ne pas spécifier de shebang ou d'adapter le shebang au système d'exploitation utilisé.
2 Michael Kjörling 07/30/2017
Je suis tombé dans le n ° 4 avec un manuscrit très récemment, et je ne pouvais pas comprendre pourquoi il ne fonctionnait pas; Après tout, les mêmes commandes fonctionnaient solidement et faisaient exactement ce que je voulais qu'elles fassent, alors que je les ai essayées directement dans la coquille. Dès que j'ai changé #!/bin/sh à #!/bin/bash , le script fonctionnait parfaitement. (À ma défense, ce script avait évolué au fil du temps d'un élément qui ne nécessitait que des shms, à celui qui s'appuyait sur un comportement semblable à celui de la bask.)
2 jlliagre 07/30/2017
@ MichaelKjörling Le (vrai) Bourne shell est presque jamais regroupé avec des distributions Linux et n'est pas compatible POSIX de toute façon. Le shell standard POSIX a été créé à partir du comportement ksh , pas de Bourne. Quelle est la plupart des distributions de Linux suite à la FHS, c'est avoir /bin/sh un lien symbolique avec le shell qu'ils sélectionnent pour fournir la compatibilité POSIX, généralement bash ou dash .

Kaz 07/31/2017.

Dans le script ./configure qui prépare le langage TXR pour la construction, j'ai écrit le prologue suivant pour une meilleure portabilité. Le script sera démarré même si #!/bin/sh est un ancien Bourne Shell non conforme au POSIX. (Je crée chaque version sur une machine virtuelle Solaris 10).

#!/bin/sh

# use your own variable name instead of txr_shell;
# adjust to taste: search for your favorite shells

if test x$txr_shell = x ; then
  for shell in /bin/bash /usr/bin/bash /usr/xpg4/bin/sh ; do
    if test -x $shell ; then
       txr_shell=$shell
       break
    fi
  done
  if test x$txr_shell = x ; then
    echo "No known POSIX shell found: falling back on /bin/sh, which may not work"
    txr_shell=/bin/sh
  fi
  export txr_shell
  exec $txr_shell $0 ${@+"$@"}
fi

# rest of the script here, executing in upgraded shell 

L'idée ici est que nous trouvons une meilleure coquille que celle que nous exécutons sous, et ré-exécuter le script en utilisant ce shell. La variable d'environnement txr_shell est définie, de sorte que le script ré-exécuté sait qu'il s'agit de l'instance récursive ré-exécutée.

(Dans mon script, la variable txr_shell est également utilisée ultérieurement, pour deux raisons exactes: premièrement, elle est imprimée dans le cadre d'un message informatif dans la sortie du script. Deuxièmement, elle est installée en tant que variable SHELL dans Makefile , de sorte que make utilisera également cette coque pour l'exécution de recettes.)

Sur un système où /bin/sh est tiret, vous pouvez voir que la logique ci-dessus trouvera /bin/bash et ré-exécuter le script avec cela.

Sur une boîte Solaris 10, le /usr/xpg4/bin/sh sera /usr/xpg4/bin/sh si aucun Bash n'est trouvé.

Le prologue est écrit dans un dialecte conservateur, en utilisant un test pour les tests d'existence de fichiers, et l'astuce ${@+"$@"} pour élargir les arguments traitant de vieux shells cassés (qui seraient simplement "$@" si nous étions dans une coque conforme POSIX).

2 comments
Charles Duffy 07/31/2017
On n'aurait pas besoin de l'horlogerie x si les citations appropriées étaient utilisées, car les situations qui obligaient cette langue à entourer maintenant - les invocations de test obsolètes avec -a ou -o combinant plusieurs tests.
Kaz 07/31/2017
@CharlesDuffy En effet; le test x$whatever que je perpétré là semble un oignon dans le vernis. Si nous ne pouvons pas faire confiance à l'ancienne coquille cassée pour faire des citations, alors la tentative finale ${@+"$@"} est inutile.

zwol 07/31/2017.

Toutes les variantes du langage Bourne sont objectivement terribles par rapport aux langages de script modernes comme Perl, Python, Ruby, node.js, et même (sans doute) Tcl. Si vous devez faire quelque chose, même un peu compliqué, vous serez plus heureux à long terme si vous utilisez l'un des éléments ci-dessus au lieu d'un script shell.

Le seul et unique avantage que la langue de shell a encore sur les langues plus récentes est que something s'appelant /bin/sh est garanti pour exister sur tout ce qui prétend être Unix. Cependant, que quelque chose peut même ne pas être conforme à POSIX; bon nombre d'entre eux ont gelé la langue implémentée par /bin/sh et les utilitaires dans le PATH par défaut prior les modifications demandées par Unix95 (oui, Unix95, il y a vingt ans et comptage). Il peut y avoir un ensemble d'outils Unix95, ou même POSIX.1-2001 si vous avez de la chance, des outils dans un répertoire qui not au PATH par défaut (p /usr/xpg4/bin Ex /usr/xpg4/bin ), mais ils ne sont pas garantis d'exister.

Cependant, les bases de Perl sont more likely d'être présentes sur une installation Unix arbitrairement sélectionnée que Bash est. (Par "les bases de Perl", je veux dire /usr/bin/perl existe et est some éventuellement assez ancienne de Perl 5, et si vous êtes chanceux, l'ensemble des modules livrés avec cette version de l'interprète est également disponible.)

Donc:

Si vous écrivez quelque chose qui doit fonctionner everywhere dans le but d'être Unix (comme un script "configuré"), vous devez utiliser #! /bin/sh #! /bin/sh , et vous devez ne pas utiliser d'extensions quelconques. De nos jours, j'écrirais le shell compatible POSIX.1-2001 dans cette circonstance, mais je serais prêt à réparer POSIXismes si quelqu'un demandait un soutien pour le fer rouillé.

Mais si vous n'écrivez not quelque chose qui doit fonctionner partout, alors vous êtes tenté d'utiliser n'importe quel bashisme, vous devriez arrêter et réécrire le tout dans un meilleur langage de script. Votre avenir vous fera plaisir.

(Donc, quand is il approprié d'utiliser les extensions Bash? Pour le premier ordre: jamais. Pour le second ordre: uniquement pour étendre l'environnement interactif Bash - par exemple, pour fournir des instructions de fin d'onglet intelligentes et de fantaisie.)

Related questions

Hot questions

Language

Popular Tags