Interprétation d'une ligne de commande

 

I. Interprétation

 

Syntaxe d'une commande simple

Dans sa forme simple une commande est écrite avec un premier mot qui définit la commande et les mots suivants sont des paramètres fournis à la commande; elle peut être complétée par des redirections d'entrée ou de sortie.
    envoiFs  toto@alamer.org  $REP/*.C
    g++ horloge.C   2> erreurs
On peut aussi modifier des valeurs de variables d'environnement, avant d'appeler la commande, qui tiendra compte du nouvel environnement. Toutefois cette modification ne concerne pas l'environnement des commandes suivantes.
    MAP=OUI ADR=toto@alamer.org envoiFs  $ADR  $REP/*.C

On peut ainsi dégager la syntaxe suivante, pour une commande simple:
    affectations   nom   liste de paramètres   redirections

Les séparateurs      ||   &   &&   ;   ;;   ( )   |   "r.chariot"      isolent des commandes simples.

Le caractère $ introduit un remplacement de paramètres, une substitution de commande ou un calcul arithmétique, sauf s'il est précédé de \.

 

Etapes d'analyse d'une ligne

  1. repérage affectations et redirections
  2. découpage en mots (voir plus loin: expansion)
  3. traitement des affectations (développer à droite de =)
  4. mise en place des redirections
  5. s'il existe une commande, elle est exécutée
        l'environnement d'exécution tient compte des affectations
        l'environnement après exécution ignore les affectations
  6. sinon l'environnement est modifié par les affectations

Comment se fait l'exécution d'une commande?
Le premier mot qui suit des affectations désigne la commande exécuté; il est analysé:

 

Expansion

L'expansion comprend 7 étapes, qui ne sont pas nécessairement toutes exécutées:
  1. Expansion accolades : (avec bash mais PAS avec sh ni ksh)

    echo file{1,2} file1 file2

  2. Expansion tilde

    echo ~astier /dptinfo/users/astier
    echo ~+ /dptinfo/users/astier/sys/itp
    On voit que ~abc est rempacé par le répertoire d'accueil de l'utilisateur "abc"; ~+ est remplacé par le répertoire courant.

  3. Remplacement des paramètres et variables

    ${12} {} est à utiliser avec plus de 9 paramètres
    echo $HOME /dptinfo/users/astier
    ${#HOME} 21 (nombre de caractères)

  4. Substitution de commande

    $(ls e*) edcomp.C edcomp.o
    `ls e*` edcomp.C edcomp.o
    Il y a équivalence entre $( ... ) et ` ... `

  5. Evaluation arithmétique (plus souple qu'avec "let")

    $((3+5*8)) 43
    echo $((`ls | wc-l` - 2)) 17     (nombre de fichiers - 2)

  6. Séparation des mots, en dehors des doubles quotes

    x="p_q   uv"  
    "a b" $x 3 mots, qui sont 'a b' 'p_q' 'uv'
    "a b" "$x" 2 mots, qui sont 'a b' 'p_q   uv'
    'a b' '$x' 2 mots, qui sont 'a b' '$x'
    Ajoutons que, dans un script, l'expression "$*" est développée en un mot, alors que "$@" est développée en autant de mots qu'il y a de paramètres.

  7. Développement des noms de fichiers, en dehors des doubles quotes
    Les caractères * ? [] ont un rôle particulier.

    e* ess3.C ess5.C ess6.cpp
    ess[2-5]* ess3.C ess5.C
    "e*" e*

II. Langage de l'interprète

Ce paragraphe concerne l'éciture de scripts, c'est-à-dire l'enchaînement de commandes écrites dans un fichier. Il existe un véritable langage, avec des variables et les structures de contrôle généralement recontrées dans les langages de programmation.

 

Variables utilisables dans un script

 

Instruction conditionnelle:   if   then   elif   then   else   fi

Dans la séquence suivante, la variable PATH est modifiée ou non, suivant la présence de "/java130/bin".
if echo $PATH  |  /java130/bin >/dev/null
then
   echo /java130/bin  déjà présent dans le chemin
else
   PATH=$PATH:/usr/java130/bin
   echo variable modifiée, \$PATH=$PATH
fi
La même modification conditionnelle est affectuée ci-dessous, sans affichage; remarquez l'utilisation de ":", qui est l'instruction vide.
if echo $PATH | grep /java130/bin >/dev/null
then :
else PATH=$PATH:/usr/java130/bin
fi
Introduisons maintenant une négation, et nous n'avons plus que le bloc "then".
Remarquez, du point de vue syntaxique, la présence du ";" entre la condition et le mot clé "then" écrits sur la même ligne.
if ! echo $PATH | grep /java130/bin  >/dev/null; then
   PATH=$PATH:/usr/java130/bin
fi
Voici enfin un exemple utilisant "elif" pour traiter une alternative multiple. La séquence ci-dessous vérifie que l'appel du script se fait avec un seul paramètre, pouvant être "-h" ou un nom ayant l'extension .cpp.
Bien réaliser que chaque condition est encadrée par "if" ou "elif" et "then".
if [ $# -ne 1 ]; then
   echo "$0 a un et un seul paramètre"
   exit 1
elif [ $1 = -h ]; then
   echo "$0 étudie un fichier source d'extension cpp"
   exit 2
elif [ $(1%\.cpp) = $1 ]; then
   echo "Il faut que l'extension soit cpp"
   exit 3
else
   : # déroulement normal
fi
 

Alternative multiple:   case ... in ... esac

L'exemple ci-dessus tente de classer le premier paramètre.
Remarquer
  > les ";;" pour terminer la suite des instructions liées à une éventualité;
  > le dernier cas "*)" pour traiter les cas non encore détectés;
  > la possibilité de plusieurs choix avec "|".
case $1 in
  *.h)  echo "$1 est un fichier d'entête"
  *.C | *.cc | *.cpp)  echo $1 est un fichier source C++ ;;
  *.[Jj]ava)  echo $1 est un fichier java
              echo "   "il faut compiler avec javac;;
  *) echo Je ne sais pas
esac
 

Répétitive:   for ... in ... do ... done

La séquence suivante affiche les fichiers du répertoire courant ayant des extensions particulières; cet affichage peut être obtenu par une seule instruction, mais ici nous présentons un exemple d'instruction répétitive.

  for i in .h .C .cpp .cc
  do
     echo "Fichiers d'extension $i":
	 ls $i 2>/dev/null
  done

La séquence suivante affiche les fichiers du répertoire courant ayant des extensions particulières, qui sont passées en paramètre au script.
Cela montre que si aucune suite n'est mentionnée dans l'instruction for, alors la liste des paramètres est utilisée.

  for ext
  do
  echo "Fichiers d'extension $ext":
	 ls $i 2>/dev/null
  done
 

Répétitive:   while ... do ... done

La séquence suivante permet de travailler sur chaque ligne du fichier "unFic"; ici le traitement est le plus simple qui soit, puisqu'on se contente d'afficher la ligne lue.
  while read ligne
  do		
       echo $ligne
  done < unFic

La partie "condition" correspond à une instruction dont le code retour représente la valeur booléenne; le corps est la suite d'instructions encadrée par les deux mots "do" et "done".

 

Fonction   function { ... }   ou   () { ... return ...}

Voici le texte de deux fonctions, qui doit précèder leur utilisation; la première effectue un affichage et la deuxième effectue une lecture

# Affiche les variables exportées dont le début du nom est fourni
function aff {
   echo chez $USER, "variable(s) trouvée(s)"
   env | grep ^$1
   }

# Affiche un message, et lit un mot
#      Renvoie un code retour non nul (FAUX) si le  mot est "."
lecture() {
  local FIN MSG
  FIN=.
  MSG="Variable commençant par (arrêt sur $FIN) : "
   echo -n $MSG
   read debut
   [ "$debut" != $FIN ]
   }

L'utilisation de ces fonctions, consiste à demander un début de mot et afficher les variables "exportables" qui commencent par les lettres lues. Cela permet de mieux connaître des variables de son environnement de travail sans avoir à lire tout l'environnement de l'interprète de commandes.
Les appels de fonction sont détectés uniquement par la présence du nom de la fonction.

while lecture
do
   aff $debut
done