Shells Unix et Posix : Introduction

Depuis le bourne shell, nombre d'évolutions ont été proposées pour programmer des scripts à partir de cette base. S'ils utilisent une syntaxe et une philosophie très proches, ces différentes évolutions posent cependant des problèmes de portabilité. Pour les règler, une norme a fini par être adoptée : IEEE POSIX.2 (ou ISO/IEC 9945-1:1990).
Ce petit cours signale les différences les plus importantes et les illustre ; il a pour but de produire le code le plus portable possible, quel que soit l'OS utilisé.

Les différences

Les tableaux suivants indiquent quels sont les différences. Attention, une fonction obsolète peut fonctionner sous un shell Posix car la norme laisse des éléments sans spécification. Cela signifie que l'éditeur est libre d'implémenter comme bon lui semble (en conservant un comportement ksh par exemple). Cependant, ce n'est pas parce que cela fonctionne sur un environnement, que cela est portable. Les fonctions obsolètes doivent être abandonnées pour prétendre à la portabilité complète.

La commande interne export
sh/ksh non POSIX

Cette commande admet des syntaxes différente en sh et ksh.
La norme POSIX rend obsolète un usage particulier : < export > envoyé sans aucun paramètre et qui donne normalement la liste des variables exportées (i.e. visibles des processus fils).

Par ailleurs, la syntaxe d'affectation du bourne shell "let variable=valeur" est obsolète en Posix.
La commande typeset du ksh (rappel de la définition des variables) est également obsolète en Posix.

Shell Posix

L'export POSIX admet les syntaxes suivantes :

#export de variables valorisées
export MA_VAR=valeur
#liste des variables exportées
export -p

L'export simple du ksh donne des résultats non garantis.


La commande interne readonly
sh/ksh non POSIX

La commande readonly envoyée sans paramètres donne normalement la liste des variables non modifiables. Elle est obsolète en shell POSIX.

POSIX

La commande correcte est : readonly -p


La commande interne exec
sh/ksh non POSIX

Sous ksh, la commande exec envoyée sans paramètres clos normalement tous les descripteurs de fichiers qui ne sont ni 0 (entrée standard), ni 1 (sortie standard), ni 2 (sortie d'erreur).
Elle est obsolète en POSIX (résultat non prévisible).

En sh, cette commande n'existe pas.

POSIX


La commande interne unset
sh/ksh non POSIX

La commande unset employée sans option sur une fonction permet d'annuler la définition de cette fonction.
Cette syntaxe est obsolète en POSIX (résultat non prévisible).
unset -v nom_variable est toujours bon.

POSIX

unset -f nom_de_la_fonction_a_oublier
unset -v nom_de_la_variable_a_oublier


Logique booléenne
sh/ksh non POSIX

Les commandes true et false (en minuscule) existent en shell non Posix.

POSIX

En Posix, true est vue comme une commande interne ( true=':' ) et false également ( false='let 0' ).


Les signaux, Pid et PGID
sh/ksh non POSIX

La commande kill (qui n'existe pas en sh) a évolué ; l'ancienne syntaxe est obsolète :
kill -num_signal pid

Celle-ci garde une compatibilité ascendante :
kill -s nom_signal pid

POSIX

Posix introduit la notion de PGID, c'est à dire un PID négatif qui indique l'ordonnancement de tous les processus d'une même session. La syntaxe devient :

kill -s nom_signal -pgid
kill -- -pgid
kill -s nom_signal pid

NB : la notion de pgid ne me semble pas claire pour l'instant. Il est nécessaire de lire la norme ISO 9945-2, paragraphe 4.32.


Command
sh/ksh non POSIX

N'existe qu'en POSIX

POSIX

Command transforme une commande interne spéciale (qui force la sortie du shell en cas d'erreur) en une commande interne régulière. Les commandes internes spéciales sont : break, continue, ., eval, exec, exit, export, readonly, return, set, shift, trap, unset, ;


function
sh/ksh non POSIX

function sert à définir une fonction en ksh

function ma_fonction()
{
print "Ceci est une fonction"
}

POSIX

function n'existe plus en shell Posix.


Evaluation d'expression avec [[ expression ]]
sh/ksh non POSIX

Cette syntaxe qui n'existe pas en sh, est obsolète en POSIX (résultat non prévisible).

POSIX

On utilisera la syntaxe simple du style :
if [ -z "$VARIABLE" ] ; then print OK ; fi


Variables PWD et OLDPWD, variable REPLY
sh/ksh non POSIX

Ces variables donnent respectivement la valeur du répertoire courant et du précédent. Elles sont obsolètes en Posix, tout comme la commande "cd -" qui revient au répertoire précédent et la fonction whence qui donne le chemin d'accès à un fichier (on préferera un find ou un whereis).

La variable REPLY (qui contient le texte entré dans un read sans variable) est obsolète en Posix.

POSIX

On utilisera $(pwd) pour le répertoire courant.


echo, print
sh/ksh non POSIX

sh ne connaît que la commande echo pour afficher, ksh préfère print.

POSIX

En posix, l'affichage se fera de préférence avec printf, echo est utilisable.


Le cas le plus problématique : les instanciations dans un pipe
sh/ksh non POSIX

En shell non Posix, la bouche suivante ne pose aucun problème :

export VAR=AVANT
print $VAR
print "Pendant" | while read VAR
do
print $VAR
VAR=APRES
done
print $VAR

On obtient bien l'affichage
AVANT
Pendant
APRES

POSIX

En Posix, le pipe provoque la création de sous-shell (processus fils) puisque l'on fork. Or dans ce cas, les instanciations faites par les fils ne se retrouvent pas dans le père, ce qui donne l'affichage :

AVANT
Pendant
AVANT

Lorsque cela est possible, la meilleure solution est de transformer la bouche while en for. Sinon, une autre solution simple est de sauvegarder des commandes d'instanciation dans un fichier temporaire (print "VAR=$VAR" >> fichier_temp) puis d'exécuter ce fichier à la sortie de la boucle. Des solutions sans fichiers sont également possibles.

Toujours dans le domaine des échanges entre processus, Posix ne supporte plus les co-processus qui permettent de faire communiquer des processus lancés simultanément. Il sera nécessaire dans ce cas d'utiliser l'option -p de read et de print.

Liens sur le sujet

Synthèse de l'AFFU