Retour au cours

portabilite

Objectifs

  • Comprendre la différence entre le shell standard POSIX (sh) et bash.
  • Identifier les fonctionnalités spécifiques à Bash (les “bashisms”).
  • Choisir le bon shebang (#!/bin/sh vs #!/bin/bash) en fonction de l’objectif de votre script.
  • Écrire des scripts plus portables qui fonctionnent sur une plus grande variété de systèmes Unix.

Explications détaillées

Notions clés

POSIX sh : Le Dénominateur Commun

POSIX est une famille de standards qui vise à maintenir la compatibilité entre les systèmes d’exploitation de type Unix. Le shell POSIX, ou /bin/sh, définit un ensemble de commandes et de fonctionnalités de base que tous les shells compatibles doivent supporter.

Pensez à sh comme le “français fondamental”. Un script écrit en pur sh a de très bonnes chances de fonctionner sur presque n’importe quel système (Linux, macOS, BSD, Solaris…).

bash : Un sur-ensemble puissant

bash (Bourne-Again SHell) est le shell par défaut sur la plupart des systèmes Linux et macOS. Il est entièrement compatible avec sh, mais il ajoute de nombreuses fonctionnalités très pratiques qui ne font pas partie du standard POSIX. Ces fonctionnalités sont appelées des “bashisms”.

Les “Bashisms” courants

Ce sont des fonctionnalités que vous utilisez probablement tous les jours, mais qui ne fonctionneront pas dans un script #!/bin/sh sur un système où /bin/sh n’est pas bash.

  • [[ ... ]] : La version moderne et sûre de [ ... ]. En sh, il faut utiliser [.
  • Les tableaux (declare -a) et tableaux associatifs (declare -A). Le shell POSIX ne connaît pas les tableaux.
  • L’expansion des accolades {a..c} ou {un,deux}.
  • La substitution de processus <(...).
  • Les boucles de style C for ((...)).
  • let et ((...)) pour l’arithmétique.
  • Le mot-clé function pour définir des fonctions (en sh, on utilise nom() { ... }).
  • Certaines options de commandes, comme echo -e.

/bin/sh : Une surprise potentielle

Sur de nombreux systèmes (comme CentOS ou macOS), /bin/sh est un lien symbolique vers /bin/bash. Sur ces systèmes, un script #!/bin/sh avec des bashisms fonctionnera… par chance. Mais sur d’autres, comme Debian ou Ubuntu, /bin/sh pointe vers dash, un shell beaucoup plus léger et strictement POSIX. Sur ces systèmes, les bashisms provoqueront des erreurs de syntaxe.

Le Shebang : Déclarer son intention

La première ligne de votre script est cruciale.

  • #!/bin/sh (ou #!/usr/bin/env sh) : Vous promettez que votre script est portable et n’utilise que des fonctionnalités POSIX.
  • #!/bin/bash (ou #!/usr/bin/env bash) : Vous déclarez que votre script a besoin de bash et que vous avez le droit d’utiliser ses fonctionnalités étendues.

Syntaxe / Usages

# Script portable avec /bin/sh
# Utilise [ ... ], des tests simples et une syntaxe POSIX
#--------------------------------------------
#!/bin/sh
set -eu

if [ "$#" -ne 1 ]; then
  echo "Usage: $0 <nom>" >&2
  exit 1
fi

NOM="$1"
echo "Bonjour, $NOM"

# Script moderne avec /bin/bash
# Utilise [[ ... ]], un tableau et une expansion de paramètre
#--------------------------------------------
#!/usr/bin/env bash
set -euo pipefail

args=("$@")
if [[ "${#args[@]}" -ne 1 ]]; then
  echo "Usage: $0 <nom>" >&2
  exit 1
fi

NOM="${args[0]}"
echo "Bonjour, ${NOM^}" # Met la première lettre en majuscule (bashism)

Bonnes pratiques

  • Soyez explicite avec le shebang. Si vous utilisez des bashisms, mettez #!/usr/bin/env bash.
  • Pour les scripts que vous devez distribuer ou qui doivent tourner sur des serveurs variés (potentiellement anciens ou minimalistes), faites l’effort de les écrire en POSIX sh.
  • Pour vos scripts personnels ou internes à votre entreprise (où bash est la norme), n’hésitez pas à utiliser les bashisms. Ils rendent le code plus sûr et plus lisible ([[ ... ]], tableaux, etc.).
  • Utilisez l’outil shellcheck. Il est excellent pour détecter les problèmes de portabilité. Il vous avertira si vous utilisez un bashism dans un script déclaré en sh.

Pièges courants

  • Le piège du /bin/sh qui pointe vers bash : Développer sur un système où /bin/sh est bash, écrire un script #!/bin/sh plein de bashisms, et le voir échouer en production où /bin/sh est dash.
  • Les options de commandes non portables : Les options de certaines commandes standards comme ps, sed ou date peuvent varier entre les versions GNU (Linux) et BSD (macOS).

Exercices

  1. Vérifiez votre système :

    • Exécutez la commande ls -l /bin/sh. Vers quoi pointe-t-elle sur votre système ? Est-ce bash, dash, ou autre chose ?
  2. Script de test de portabilité :

    • Créez un fichier test_portabilite.sh avec le shebang #!/bin/sh.
    • À l’intérieur, écrivez une ligne qui utilise un bashism évident, comme mon_tableau=("a" "b") ou if [[ 1 == 1 ]].
    • Essayez de l’exécuter avec bash ./test_portabilite.sh (ça devrait marcher).
    • Maintenant, essayez de l’exécuter avec un shell strictement POSIX comme dash (si disponible : dash ./test_portabilite.sh). Observez l’erreur.
  3. Réécriture portable :

    • Prenez ce script Bash :
      #!/usr/bin/env bash
      set -eu
      
      if [[ $# -eq 0 ]]; then
        echo "Aucun argument"
        exit 1
      fi
      
      for arg in "${@}"; do
        echo "Arg: ${arg,,}" # tout en minuscules
      done
    • Réécrivez-le pour qu’il soit compatible #!/bin/sh. (Indices : [[ -> [, ,, -> tr '[:upper:]' '[:lower:]').