Blog ENI : Toute la veille numérique !
-25€ dès 75€ sur les livres en ligne, vidéos... avec le code FUSEE25. J'en profite !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Linux
  3. Programmation et scripts Bash
Extrait - Linux Principes de base de l'utilisation du système (8e édition)
Extraits du livre
Linux Principes de base de l'utilisation du système (8e édition) Revenir à la page d'achat du livre

Programmation et scripts Bash

Introduction

Ce chapitre explique comment automatiser l’exécution de travaux à partir des éléments étudiés précédemment. Les fonctionnalités abordées permettront à l’utilisateur de créer ses propres scripts shell, programmes développés en langage shell.

En outre, le lecteur pourra comprendre et personnaliser les nombreux scripts déjà fournis avec le système Linux.

Scripts shell

Un script Bash est un fichier texte contenant une suite de commandes telles que celles exécutées sur la ligne de commande.

Toute ligne de commande valide peut faire l’objet d’un script shell pour un traitement par lot (batch). Inversement, tout code écrit dans un script Bash peut être saisi directement sur la ligne de commande.

Dans cet exemple, le fichier script porte le nom prog.sh :

[nicolas]$ cat prog.sh 
date 
whoami 
var=12 
echo $var 

1. Appel et exécution

Les modifications de l’environnement du shell appelant dépendent de la manière dont le script shell est appelé.

bash prog.sh

La première façon d’appeler un script shell consiste à lancer un nouveau bash avec le nom du script en argument. Ce nouveau shell traite alors les instructions contenues dans le fichier comme si elles avaient été saisies au clavier.

Il en résulte la création d’un sous-processus bash qui se termine automatiquement à la fin de l’exécution du script.

[nicolas]$ cat prog.sh 
date 
whoami 
var=12 
echo $var 
[nicolas]$ echo $var 
 
[nicolas]$ bash prog.sh 
dim. avril 1 16:01:06 CEST 2018 
nicolas 
12 
[nicolas]$ echo $var 

Toute modification de l’environnement - comme le changement de répertoire ou la définition de variables - n’intervient que sur le sous-shell créé temporairement. Il n’y a aucune modification du shell initial.

On peut représenter ce type d’appel de script comme suit :

images/1001scr1.png

Le premier shell lancé sur un terminal (appelé shell de connexion) est préfixé par un tiret dans la liste des processus. C’est la raison pour laquelle son nom est -bash dans le schéma précédent, alors que le sous-shell est nommé bash.

Le shell bash est un sous-processus du shell de connexion -bash. De même, les commandes externes date et whoami sont des sous-processus du shell bash tandis que l’affectation de la variable var et son affichage...

Codes retour

Toute commande sous Linux retourne un code à la fin de son exécution. Ce code témoigne du bon déroulement de son exécution : zéro lorsque le programme s’est terminé correctement, différent de zéro dans le cas contraire.

Le code retour d’une commande est une valeur comprise entre 0 et 255.

Une même commande peut renvoyer différents codes si son exécution s’est mal déroulée, ceci pour plusieurs raisons. Par exemple, la commande cp peut échouer parce que le fichier source est inexistant, les droits d’écriture sur le répertoire cible insuffisants, le système de fichiers saturé, etc.

La variable spéciale $? contient le code retour de la dernière commande exécutée :

[nicolas]$ pwd 
/home/nicolas 
[nicolas]$ echo $? 
0 
[nicolas]$ ls zorglub 
ls: zorglub: No such file or directory 
[nicolas]$ echo $? 
1 
[nicolas]$ echo $? 
0 

Le dernier code retour est bien zéro car il indique que la commande précédente (echo $?) s’est bien déroulée. Pour réutiliser ultérieurement le code retour d’un programme, il faut prendre soin de l’enregistrer dans une variable :

[nicolas]$ ls zorglub 
ls: zorglub: No such file or directory 
[nicolas]$ cr=$? 
[nicolas]$ pwd ...

Enchaînement de commandes

1. Exécution séquentielle

Au lieu de saisir les commandes les unes après les autres et d’attendre la fin de leur exécution avant de lancer la suivante, il est possible d’enchaîner plusieurs commandes sur la même ligne, en les séparant par un point-virgule :

[nicolas]$ date 
dim. avril 1 16:06:40 CEST 2018  
[nicolas]$ ps 
  PID TTY          TIME CMD 
 4511 pts/1    00:00:00 bash 
 4613 pts/1    00:00:00 ps 
[nicolas]$ date ; ps 
dim. avril  1 16:07:12 CEST 2018  
  PID TTY          TIME CMD 
 4511 pts/1    00:00:00 bash 
 4624 pts/1    00:00:00 ps 

Ces enchaînements sont pratiques lorsque plusieurs commandes longues doivent être lancées successivement, sans que l’utilisateur ait à intervenir.

Les espaces autour du ; sur la ligne de commande ne sont pas obligatoires mais apportent une meilleure lisibilité.

Dans l’enchaînement cmd1 ; cmd2 ; cmd3, la commande cmd2 n’est exécutée qu’à la fin de la commande cmd1 ; de même, cmd3 est lancée lorsque cmd2 est terminée. Par contre, il n’y a aucun lien entre...

Variables spéciales

Certaines variables sont définies par le shell et peuvent être référencées par l’utilisateur dans les scripts shell.

1. $$, $PPID

La variable spéciale $$ contient le PID du shell en cours d’exécution tandis que $PPID (Parent Process ID) donne le PID de son processus père :

[nicolas]$ cat prog.sh 
#!/bin/bash 
echo "mon PID : $$" 
echo "mon PPID : $PPID" 
[nicolas]$ echo $$ 
1223 
[nicolas]$ echo $PPID 
1222 
[nicolas]$ ./prog.sh 
mon PID : 1380 
mon PPID : 1223 
[nicolas]$ . ./prog.sh 
mon PID : 1223 
mon PPID : 1222 

2. $0

La variable $0 contient le nom du script en cours d’exécution tel qu’il a été appelé sur la ligne de commande :

[nicolas]$ cat prog.sh  
#!/bin/bash 
echo "mon nom : $0" 
[nicolas]$./prog.sh  
mon nom : ./prog.sh 
[nicolas]$ /home/nicolas/prog.sh  
mon nom : /home/nicolas/prog.sh 

3. $1, $2, $3, ...

Les variables $1, $2, $3, etc. représentent les paramètres positionnels du shell, c’est-à-dire les arguments passés au script sur la ligne de commande :

[nicolas]$ cat prog.sh 
#!/bin/bash 
echo "premier paramètre : $1" 
echo "deuxième paramètre : $2" 
echo "troisième paramètre...

Commande test

La commande test permet d’effectuer un ensemble de tests sur les fichiers, les chaînes de caractères, les valeurs arithmétiques et l’environnement utilisateur.

Cette commande a un code retour égal à zéro lorsque le test est positif, et différent de zéro dans le cas contraire ; ceci permet de l’utiliser dans des enchaînements de commandes avec exécution conditionnelle (&& et ||) ou dans les structures de contrôle qui seront étudiées plus loin.

La commande test possède deux syntaxes : test expression et expression ]expression représente le test à effectuer.

La seconde syntaxe offre une lecture plus aisée des conditions dans les structures de contrôle.

Les espaces après le crochet ouvrant et avant le crochet fermant sont obligatoires dans la syntaxe expression  ]. D’une manière générale, tous les éléments de syntaxe de la commande test doivent être séparés par au moins un espace.

La suite de la section présente les principaux opérateurs composant les expressions de test de la commande.

1. Test de fichiers

-f fichier

Retourne vrai (code retour égal à zéro) si le fichier est de type standard (file) :

[nicolas]$ test -f /etc/passwd 
[nicolas]$ echo $? 
0 
[nicolas]$ [ -f /etc ] || echo "/etc n'est pas un fichier standard" 
/etc n'est pas un fichier standard 

-d fichier

Retourne vrai si le fichier est de type répertoire (directory) :

[nicolas]$ fichier='/etc' 
[nicolas]$ [ -d "$fichier" ] && echo "$fichier est un répertoire" 
/etc est un répertoire 

Lorsqu’on teste le contenu d’une variable, il est préférable d’encadrer celle-ci de guillemets pour éviter une erreur dans la syntaxe de la commande test si la variable n’est pas définie.

-r fichier

Retourne vrai si l’utilisateur possède le droit de lire le fichier (read).

-w fichier

Retourne vrai si l’utilisateur possède le droit de modifier le fichier (write).

-x fichier

Retourne vrai si l’utilisateur...

Opérations arithmétiques

Comme tout langage de programmation, le Bash offre les outils nécessaires au calcul arithmétique.

Pour cela, il existe principalement les commandes expr, let et bc.

1. expr

expr est une ancienne commande externe du Bash et n’est présentée ici que succintement car on lui préfère la commande let qui offre une syntaxe moins contraignante.

Cette commande renvoie sur sa sortie standard le résultat des expressions arithmétiques passées en argument. Sa syntaxe est expr expression.

Tous les éléments de l’expression doivent être séparés par au moins un espace et certains opérateurs arithmétiques sont préfixés par un antislash pour éviter toute confusion avec les caractères spéciaux du shell.

Opérateurs arithmétiques

Les opérateurs arithmétiques sont :

  • + : addition

  • - : soustraction

  • \* : multiplication

  • / : division entière

  • % : reste de la division entière ou modulo

  • \( et \) : parenthèses

Une substitution de commandes est généralement utilisée pour affecter le résultat de la commande expr à une variable. On obtient par exemple :

[nicolas]$ expr 2 + 3 
5 
[nicolas]$ expr 2 - 3  
-1 
[nicolas]$ expr 2 + 3 \* 4 
14 
[nicolas]$ expr \( 2 + 3 \) \* 4 
20 
[nicolas]$ resultat=$(expr 9 / 2)  
[nicolas]$ echo $resultat 
4 
[nicolas]$ expr $resultat % 3 
1 

Opérateurs logiques

Les opérateurs logiques sont :

  • \| : ou logique

  • \& : et logique

  • \< : strictement inférieur

  • \<= : inférieur ou égal...

Commande read

La commande read interrompt l’exécution du shell jusqu’à ce que l’utilisateur ait introduit une chaîne de caractères (même vide) sur son entrée standard.

Les mots composant la chaîne de caractères saisie par l’utilisateur sont affectés aux variables dont les noms sont passés en argument à la commande read.

[nicolas]$ read a b c 
1 2 3 
[nicolas]$ echo $a ; echo $b ; echo $c 
1 
2 
3 

S’il y a plus de mots que de variables, la dernière variable contiendra la fin de la chaîne de caractères. Dans le cas inverse, les variables supplémentaires seront nulles :

[nicolas]$ read a b c 
1 2 3 4 
[nicolas]$ echo $a ; echo $b ; echo $c 
1 
2 
3 4 
[nicolas]$ read a b c 
1 2 
[nicolas]$ echo $a ; echo $b ; echo $c 
1 
2 

Si la commande read est appelée sans argument, la réponse de l’utilisateur est affectée à la variable d’environnement $REPLY :

[nicolas]$ read 
réponse de l'utilisateur 
[nicolas]$ echo $REPLY 
réponse de l'utilisateur 

Il est possible de faire précéder la saisie d’une phrase avec l’option -p (prompt) de la commande read :

[nicolas]$ read -p "âge du capitaine ? " age 
âge du capitaine ? 12 
[nicolas]$ echo $age ...

Structures de contrôle

Les structures de contrôle permettent d’exécuter une ou plusieurs commandes suivant le résultat d’une expression.

L’expression fournie comme condition de la structure peut être n’importe quelle commande ; c’est le code retour de cette commande qui est déterminant. On utilise principalement les commandes test ou let comme conditions.

Seules les instructions if, for et while sont présentées ici.

1. L’instruction if

L’instruction if exécute une série de commandes si la condition indiquée est vraie.

La syntaxe générale est :

if condition 
then 
      série de commandes si condition vraie 
else 
      série de commandes si condition fausse 
fi 

Chaque mot-clé de la structure (if, then, else et fi) doit se trouver sur une ligne distincte mais la clause else n’est pas obligatoire.

Voici un exemple d’utilisation :

[nicolas]$ cat prog.sh 
#!/bin/bash 
if [ "$1" = "glop" ] 
then 
        echo "c'est bien" 
else 
        echo "c'est pas bien" 
fi 
[nicolas]$ ./prog.sh glop 
c'est bien 
[nicolas]$ ./prog.sh pasglop 
c'est...

Exercice

Exercice

Pour chaque extension .conf, .cfg et .d, indiquez s’il y a plus de 10 fichiers ou non, dont le nom se termine avec cette extension dans le répertoire /etc.

Solution

La solution utilise une boucle for pour parcourir avec la variable $ext les différentes extensions proposées dans l’énoncé.

[nicolas]$ for ext in .conf .cfg .d 
> do 
>   [[ $(ls -d /etc/*$ext | wc -l) -gt 10 ]] \ 
>     && echo "il a plus de 10 fichiers se terminant par $ext" \ 
>     || echo "il a moins de 10 fichiers se terminant par $ext" 
> done 
il a plus de 10 fichiers se terminant par .conf 
il a moins de 10 fichiers se terminant par .cfg 
il a plus de 10 fichiers se terminant par .d 

La commande ls -d /etc/*$ext | wc -l, qui retourne le nombre de fichiers correspondants, est substituée ($(...)) par son résultat dans le test ([[... -gt 10 ]]) qui, lui, détermine s’il y en a plus de 10.

Si ce test est vrai (&&), on exécute la commande echo "...plus..." ; sinon (||), on exécute la suivante : echo "...moins...".