Les scripts sous Unix

Introduction aux scripts bash

Attention: Ce contenu est un complément des documents ‘Les scripts Linux (P1 et P2)’, disponibles sur LEA.  Utilisé seul, il ne permet pas d’atteindre la compétence « Scripts Bash’ du cours 420-1C5.

Il est important de consulter les documents P1 et P2 pour être en mesure de réussir l’épreuve synthèse.


Partie 1

Note: Avant de commencer, afficher le ‘shell’ courant avec

$ echo $SHELL
/bin/bash


Exemple 01
– Affichage

#!/bin/bash
# La ligne précédente est nommée 'script shebang ou bang line'

# Ce script efface le terminal et affiche des info à l'écran

clear				# Efface l'écran

echo "Exemple 01"
echo "----------------------------------------------------------"
echo "Début du script..."

echo "Bonjour, $USER!"		# $ pour obtenir le contenu d'une variable
echo                            # Affiche une ligne vide

echo "Voici la liste des utilisateurs connectés:"
echo							
w				
echo				

echo "Initialisation de deux variables:"
COLOUR="rouge"					
VALUE="3.14159256"					
echo -n "La couleur est $COLOUR et "	# -n = pas de retour de chariot	
echo "le nombre est $VALUE"		
echo 

echo "Voilà, retour à l'invite."
echo "----------------------------------------------------------"
echo


Exemple 01.2
– Insérer des caractères de contrôle dans une chaine.

# Utilisation de echo -e
echo '------------------------------------------'
echo "Bonjour \n$USER !"
echo '------------------------------------------'
echo -e "Nous sommes le \n" ; date
echo '------------------------------------------'
echo -e "Bonjour \t$USER dont le UID est \n$UID"
echo '------------------------------------------'
echo -e 'Bonjour \t$USER dont le UID est \n$UID'
echo '------------------------------------------'


Exemple 02
– Déverminage d’un script

# Tout le script
bash -x exemple01

# Une partie
# Utilisation de set -+x dans le script


set -x			# déverminage à partir d'ici
w
set +x			# fin du déverminage


Exemple 03
– Passer des paramètres au script

#Exemple03
#!/bin/bash
# Description: Afficher un message à partir de valeurs reçues en entrée.
# ----------------------------------------------------------------------
# Param 1 : prénom
# Param 2 : nom
# Usage: ./Exemple02 prenom nom
# ----------------------------------------------------------------------
echo "Bonjour $1 $2, bienvenue au cours sur les scripts bash."
echo 'Bonjour $1 $2, bienvenue au cours sur les scripts bash.'


Exemple 03.2
– Boucler sur les paramètres reçus

# Traiter tous les paramètres reçus avec 'for in do'
compteur=1
for num in "$@"
 do
   echo "param $compteur = $num"
   ((compteur++))
 done


Exemple 04
– Instruction ‘if’ – Vérifier si le script a reçu des paramètres

#Exemple04
#!/bin/bash
# Description: Vérifier si le script a reçu des paramètres
# ----------------------------------------------------------------------
# Param 1 : prénom
# Param 2 : nom
# Usage: ./Exemple04 
# ----------------------------------------------------------------------
if [ $# -eq 0 ]   # si nb param == 0 alors
  then
    echo "Usage: ./Exemple02 prenom nom"
    exit 1        # sortir du script avec un code d'erreur
fi
echo "Bonjour $1 $2, bienvenue au cours sur les scripts bash."

Il est aussi possible de vérifier si l’argument no 1 est vide:

if [ -z "$1" ]
  then
    echo "Désolé pour vous, mais il n'y a pas d'argument :-( !"
fi


Exemple 05
– Saisie à partir du clavier

#!/bin/bash
#Exemple05 -  instruction read - Saisie à partir du clavier
echo '--------------------------------------'
echo "Entrez un prénom suivi du nom"
read prenom nom 
echo
echo "Bonjour $prenom $nom"
echo '--------------------------------------'
exit 0


Exemple 05.2
– Validation de la saisie avec [[ ||, &&]]

#!/bin/bash
#Exemple05.2 -  instruction read - Saisie à partir du clavier
echo '--------------------------------------'
echo "Entrez un prénom suivi du nom"
read prenom nom 
# Note: le double [[]] permet l'utilisation de filtre et d'une syntaxe moderne.
# Par exemple  [[ $nom = "m"* ]] -> nom débute par 'm'.  || pour OR et && pour AND 
if [[ "$prenom" == "" || "$nom" == "" ]]  
  then
  echo "Erreur: Il faut enter un prénom suivi d'un nom!!"
  exit 1
fi

echo
echo "Bonjour $prenom $nom"
echo '--------------------------------------'
exit 0

Note: Les expressions complexes doivent être entre [[ … ]].  || = OR


Exemple 05.3
– Exemple 5.2 avec syntaxe ancienne []

#!/bin/bash
#Exemple05c -  instruction read - Saisie à partir du clavier
echo '--------------------------------------'
echo "Entrez un prénom suivi du nom"
read prenom nom 
# Note: -0 = OR, -a = AND

if [ -z "$prenom" -o -z "$nom" ]  
  then
  echo "Erreur: Il faut enter un prénom suivi d'un nom!!"
  exit 1
fi

echo
echo "Bonjour $prenom $nom"
echo '--------------------------------------'
exit 0


Exemple 06
– Les variables d’environement

# Voici quelques variables d'environnement 
# Liste obtenue avec le commande 'set'

BASH_VERSION
HISTFILE
HOME
LOGNAME

$ echo $BASH_VERSION

# -----------------------------------------------
# Définir une variable d'ENV:

# Note:  Pas d'espace lors de la déclaration
$ maVariable="Je suis moi"

$ echo $maVariable

# -------------------------------------------------
# Utilisation de caractères spéciaux dans la chaine

$ message="Ce \"texte\" \\n contient des car spéciaux"


$ msg3="Ligne 1\nLigne 2"
$ echo -e "$msg3"

# --------------------------------------------------------
# Définir une variable à partir du résultat d'une commande 
$ ladate=$(date +%Y-%m-%d)
$ echo $ladate

# Autre syntaxe possible (plus ancienne) - apostrophe inversé

$ liste=`ls`
$ echo $liste

# -----------------------------------------------
# Rendre la variable disponible pour les scripts
# La commande 'export' nomVariable

$ export maVariable

# -----------------------------------------------
# Supprimer une variable

$ unset maVariable

# -----------------------------------------------
# Substitution de variables
tp="/version"
echo $tp
ver=$tp1		# la variable tp1 n’existe pas alors ver = une chaine vide, 
echo $ver 		# la variable 'ver' n'a pas été créée!
ver=${tp}1		# Substitution de la variable tp
echo $ver 		# /version1

# Note, il est aussi possible de l'écrire ainsi:
ver="$tp"1


Exemple 07
– Les tableaux

# Déclarer un tableau
$ amis[0]=Bob
$ amis[1]=Bob
$ amis[2]=Toto
$ amis[3]=France

$ echo ${amis[0]}

# Afficher tous les éléments d'un tableau
$ echo ${amis[@]}

Partie 2

L’instruction ‘if’

Les quatre formes de l’instruction if:

Forme 1 – Expression arithmétique, avec (())

Forme  2 – Tester la valeur de retour d’une commande, avec ()

Forme 3 – Un test simple (ancienne syntaxe), avec [  ]

Forme 4 – Un test avec une syntaxe plus complexe et moderne, avec [[  ]]

Exemple 08 – L’instruction ‘if’ – Tester des valeurs entières

# Utilisation de la commande 'if'
# NOTE: [] est un synonyme de la commande 'test'
# Voir: man test
# --------------------------------------
# un si simple
# if [ test ]
# then
#  echo "le test est passé"
# fi # on ferme toujours la condition par fi (if en verlan !)

# et avec un else if et un else
# if [ test ]
# then
#   echo "le test est passé"
# elif [ test2 ]
#   echo "le test2 est passé"
# else
#  echo "les deux tests ont échoué"
##fi
# --------------------------------------

num=10  # Definir une variable numérique

echo "--------------------------------------"
echo -e "Test sur un nombre:\n\n"
if [ $num -lt 99 ]
then
  echo "la variable est inférieure à 99"
fi

if [ $num -eq 11 ]
then
  echo "la variable est égale à 11"
fi
echo "--------------------------------------"


# Synthase compacte
if [ "test" == "test" ]; then
  echo -ne "\nle test: "
  echo -n "if [ \"test\" == \"test\" ] "
  echo -e "est passé!\n"
fi

echo "--------------------------------------"

Exemple 09 – Tester si un fichier est présent dans le répertoire

#!/bin/bash
#Exemple09 -  Vérifier si un fichier est présent

clear
echo '--------------------------------------'
echo -n "Entrez le nom d'un fichier: "
read fichier 
if [ -a "$fichier" ]  
  then
  echo -e "\nLe fichier $fichier est présent dans le dossier $(pwd)"
  else
  echo -e "\nErreur: Le fichier $fichier n'est pas présent dans le dossier $(pwd)"
fi
echo

Partie 3 – Utilisation de la commande ‘test’

Note: Voir le document de cours pour les détails sur l’utilisation de la commande ‘test’

Exemple 10 – Tester des valeurs numériques

#Exemple 10
#!/bin/bash
# Comparer deux nombres avec l'instruction 'test'
clear
PI=31415
NBOR=16180

echo "Ce script bash compare deux nombres avec la commande 'test'"
echo "-----------------------------------------------------------"
echo

# Note: Bash supporte seulement les nombres entiers.
if test $PI -lt $NBOR                   # PI < NBOR ?
then
        echo "$PI est plus petit que $NBOR"
elif test $PI -gt $NBOR                 # PI > NBOR ?
then
        echo "$PI est plus grand que $NBOR"
else
        echo "$PI est égal à $NBOR"
fi

Note:  Il est possible de vérifier que la chaine saisie contient seulement des caractères numérique:

if [[ $nb == ?(-)+([0-9]) ]];then ; echo "$nb est un entier" ; fi

Référence: Bash Pattern matching


Exemple 10.2
– Tester si une chaine est vide

#Exemple 10.2
#!/bin/bash
# Saisir deux nombres et les comparer avec l'instruction [ test ]
clear
echo -n "Entrez deux nombres entiers séparés par un espace: " 
read nb1 nb2

# Tester si deux valeurs ont été saisies:
# Combinaison de tests avec && (AND) et || (OR)
# test -z, tester si une chaine est vide
if [ -z $nb1 ] || [ -z $nb2 ] ; then
        echo "Erreur:  Il faut fournir deux nombres!!"
        exit 1
fi

# Note: Bash supporte seulement les nombres entiers.
if test $nb1 -lt $nb2                   # nb1 < nb2 ?
then
        echo "$nb1 est plus petit que $nb2"
elif test $nb1 -gt $nb2                 # nb1 > nb2 ?
then
        echo "$nb1 est plus grand que $nb2"
else
        echo "$nb1 est égal à $nb2"
fi

Exemple 11 – Vérifier si un groupe existe dans le fichier /etc/group

#Exemple 11
#!/bin/bash
# Saisir un nom de groupe puis vérifier s'il existe avec la commande 'grep'
clear
echo -n "Entrez le nom d'un groupe: "
read groupe

# Tester si le nom du groupe a été saisie:
if  test -z $groupe  ; then
        echo "Erreur:  Il faut fournir le nom d'un groupe!"
        exit 1
fi


# Note: grep -q = ne pas afficher le résultat à l'écran
if grep -q $groupe /etc/group
then
  echo "Le groupe: $groupe est présent dans le fichier /etc/group"
else
  echo "Le groupe: $groupe n'est pas présent dans le fichier /etc/group"
fi


#------------------------------------------------------
# Autre façon de tester: grep -E "^$groupe:" /etc/group

Exemple 12 – Valeur de retour d’une commande

#Exemple 12

echo "Ce script retourne le nombre de paramètres reçus"
echo "Cette valeur sera disponible, à la sortie du script, dans la variable '\$?'"

# $# = nb de param reçu.
exit $#

$ echo $?

Note: La variable $? est l’équivalent de ERRORLEVEL sous CMD de Windows.


Partie 4 – La commande ‘case’

Exemple 13 – Valider un choix multiple

#Exemple13
#!/bin/bash
clear
echo    "Exemple d'utilisation de la commande 'case'"
echo -e "-------------------------------------------\n"
echo "---> case - exemple 1"
read -p "Effacer le disque rigide O/N? " jaipeur

case $jaipeur in
# Note: Uitlisation de la substitution de variable pour former les mots oui et non.
 o|O)   echo "Vous avex répondu ${jaipeur}ui :--( !" ;;
 n|N)   echo "Sage décision d'avoir répondu ${jaipeur}on" ;;
   *)   echo "Réponse erronée!" ; exit
esac


echo "---> case - exemple 2"
read -p "Veuillez entrer une chaîne de caractères: " param

case $param in
 0|1|2|3|4|5|6|7|8|9) echo "$param est un chiffre" ;;
          [0-9][0-9]) echo "$param est un nombre à deux chiffres" ;;
           [a-zA-Z]*) echo "$param est un mot" ;;
                   *) echo "$param est non prévu" ;;
esac

Note: L’exemple précédent valide seulement des nombres à un ou deux caractères de long.  Voici une solution plus élégante:


Exemple 14
– Utilisation de ‘shell pattern matching‘.

# Exemple 14
# Projet: 420-1C5.bash-file
# Auteur: Alain Boudreault
# Date: 2020.12.06
# Description: Tester un nombre avec 'pattern matching'
# -----------------------------------------------------
# Note: Pour permettre le 'pattern matching' 
# La ligne suivante est nécessaire sous bash
shopt -s extglob

# Boucle principale du programme
while true; do
  clear
  echo -n "Entrez un nombre long: "
  read unNombre
    case $unNombre in

      ( +([0-9]) ) echo "$unNombre est un nombre valide"
                   break 2 ;;  # // sortir des 2 blocs de code
                *) echo "$unNombre est invalide!"
                   echo "Appuyer sur retour pour recommencer ..."; read;;
    esac # Fin du case

done # Fin du while

# break 2 pointe ICI
echo "--- Fin du programme ---"

Partie 5 – La commande while

Exemple 15 – Une boucle while tant que fichier n’est pas trouvé

#!/bin/bash
#Exemple15

# Début du programme
clear
echo -n "Entrez un nom de fichier: "
read fichier

while [ ! -e "$fichier" ] # Boucler tant que le fichier est inexistant
do 
  echo "Fichier $fichier introuvable, appuyer sur retour pour recommencer..."
  read
  clear
  echo -n "Entrez un nom de fichier: "
  read fichier
done

echo
echo "--------------------------------------------------------"
echo "Le fichier $fichier est présent sur le volume."
echo

Exemple 15b – Normalisation de l’exemple précédent – Utilisation d’une fonction

#!/bin/bash
#Exemple15b

# Les fonctions
function lireNomFichier () {
  echo "Entrez un nom de fichier"
  read fichier
}

# Début du programme
clear

lireNomFichier

while [ ! -e "$fichier" ] # Boucler tant que le fichier est inexistant
do 
  echo "Fichier $fichier introuvable, appuyer sur retour pour recommencer..."
  read
  clear
  lireNomFichier
done

echo
echo "--------------------------------------------------------"
echo "Le fichier $fichier est présent sur le volume."
echo


Exemple 15c
– Une boucle sans fin

while true; do  # ctrl+c pour quitter
  echo "Je suis une boucle sans fin -  ;-)"
done


Exemple 15d
– Afficher l’heure dans une boucle while

#Exemple15d
#!/bin/bash
# Afficher l'heure à toutes les secondes
while true
do
  clear
  echo -n "Bonjour $USER, il est: ";
  date +"%H.%m.%S"
  echo "CTRL+C pour quitter"
  sleep 1
done


Exemple 16
– Boucler sur un contenu de redirection

#Exemple16
#!/bin/bash
fich="/etc/passwd"
numeroLigne=1

while read ligne
do
  if [[ $ligne = "m"* ]]  # Afficher la ligne si le texte commence par 'm'
  then
    echo "----------------------------------------------------------------"
    echo "Résultat trouvé à la ligne $numeroLigne"
    echo $ligne
    echo "----------------------------------------------------------------"
  fi
((numeroLigne+=1))
done < "${fich}"


Exemple 17
– until do … done

#!/bin/bash
#Exemple17

until
  echo -e "Entrez le nom du fichier à afficher"
  read fich
  [ -e "$fich" ] # NOTE: Boucle tant que cette condition est fausse
do 
   echo -e "Fichier inexistant, saisie à recommencer..." 
done

echo "---------------------------------------------------------"
echo "Voici le contenu du fichier: $fich"
echo
cat $fich


Exemple 18
– choix multiple via un menu, select in …

#Exemple18
#!/bin/bash
# Exemple d'utilisation de l'instruction select
PS3="Entrez le numéro de votre choix -> "
echo "Que désirez-vous boire ?"
select boisson in "Rien, merci" "Café" "Thé au lait" "Chocolat"
do
	if [ -z "$boisson" ]
	then
		echo "Erreur : entrez un des chiffres proposés."
	elif [ "$REPLY" -eq 1 ]
	then
		echo "Au revoir !"
		break
	else
		echo "Vous avez fait le choix numéro $REPLY ..."
		echo "Le $boisson vous sera servi dans un instant !"
		echo "Autre chose ?"
	fi
	echo
done

Laboratoire

La commande ‘getent‘ permet de vérifier des paramètres systèmes.

Par exemple, pour vérifier si l’utilisateur ‘toto’ existe;

$ getent passwd toto

Si la commande retourne une valeur autre que  ‘0’ c’est que l’opération n’a pas réussit.

$ echo $?

$ 2

Pour supprimer les messages, normaux et erreurs, d’une commande:

$ getent passwd toto &> /dev/null
$ addgroup 456abc  &> /dev/null

Voir la documentation ‘man getent’

Action – Écrire un script ‘creerGroupes’;

Voici ce que produit le script:

Exemple 1 - avec des groupes existants

$ sudo ./creerGroupes users root

----------------------------------------
Traitement du groupe 'users'
Erreur: Le groupe 'users' existe déjà

----------------------------------------
Traitement du groupe 'root'
Erreur: Le groupe 'root' existe déjà

0 groupe sur 2 de créé


Exemple 2 - avec un nom invalide

$ sudo ./creerGroupes 444abc

----------------------------------------
Traitement du groupe '444abc'
Erreur: Nom du groupe invalide: '444abc'

0 groupe sur 1 de créé


Exemple 3 - avec trois nouveaux groupes valides

$ sudo ./creerGroupes groupe01 groupe02 groupe99

----------------------------------------
Traitement du groupe 'groupe01'
Création du groupe 'groupe01' réussit

----------------------------------------
Traitement du groupe 'groupe02'
Création du groupe 'groupe02' réussit

----------------------------------------
Traitement du groupe 'groupe99'
Création du groupe 'groupe99' réussit

3 groupes sur 3 de créés


Note:  Il y aura une question semblable à l’épreuve synthèse.


Sortie et reprise d’une boucle – Voir le document pdf  ‘Les scripts Linux (P2)’

Les fonctions – Voir le document pdf  ‘Les scripts Linux (P2)’

Divers – Voir le document pdf  ‘Les scripts Linux (P2)’


Référence pour la commande ‘test’ [] – $ man test

test: test [expr]
    Evaluate conditional expression.
    
    Exits with a status of 0 (true) or 1 (false) depending on
    the evaluation of EXPR.  Expressions may be unary or binary.  Unary
    expressions are often used to examine the status of a file.  There
    are string operators and numeric comparison operators as well.
    
    The behavior of test depends on the number of arguments.  Read the
    bash manual page for the complete specification.
    
    File operators:
    
      -a FILE        True if file exists.
      -b FILE        True if file is block special.
      -c FILE        True if file is character special.
      -d FILE        True if file is a directory.
      -e FILE        True if file exists.
      -f FILE        True if file exists and is a regular file.
      -g FILE        True if file is set-group-id.
      -h FILE        True if file is a symbolic link.
      -L FILE        True if file is a symbolic link.
      -k FILE        True if file has its `sticky' bit set.
      -p FILE        True if file is a named pipe.
      -r FILE        True if file is readable by you.
      -s FILE        True if file exists and is not empty.
      -S FILE        True if file is a socket.
      -t FD          True if FD is opened on a terminal.
      -u FILE        True if the file is set-user-id.
      -w FILE        True if the file is writable by you.
      -x FILE        True if the file is executable by you.
      -O FILE        True if the file is effectively owned by you.
      -G FILE        True if the file is effectively owned by your group.
      -N FILE        True if the file has been modified since it was last read.
    
      FILE1 -nt FILE2  True if file1 is newer than file2 (according to
                       modification date).
    
      FILE1 -ot FILE2  True if file1 is older than file2.
    
      FILE1 -ef FILE2  True if file1 is a hard link to file2.
    
    All file operators except -h and -L are acting on the target of a symbolic
    link, not on the symlink itself, if FILE is a symbolic link.
    
    String operators:
    
      -z STRING      True if string is empty.
    
      -n STRING
         STRING      True if string is not empty.
    
      STRING1 = STRING2
                     True if the strings are equal.
      STRING1 != STRING2
                     True if the strings are not equal.
      STRING1 < STRING2
                     True if STRING1 sorts before STRING2 lexicographically.
      STRING1 > STRING2
                     True if STRING1 sorts after STRING2 lexicographically.
    
    Other operators:
    
      -o OPTION      True if the shell option OPTION is enabled.
      -v VAR         True if the shell variable VAR is set.
      -R VAR         True if the shell variable VAR is set and is a name
                     reference.
      ! EXPR         True if expr is false.
      EXPR1 -a EXPR2 True if both expr1 AND expr2 are true.
      EXPR1 -o EXPR2 True if either expr1 OR expr2 is true.
    
      arg1 OP arg2   Arithmetic tests.  OP is one of -eq, -ne,
                     -lt, -le, -gt, or -ge.
    
    Arithmetic binary operators return true if ARG1 is equal, not-equal,
    less-than, less-than-or-equal, greater-than, or greater-than-or-equal
    than ARG2.
    
    See the bash manual page bash(1) for the handling of parameters (i.e.
    missing parameters).
    
    Exit Status:
    Returns success if EXPR evaluates to true; fails if EXPR evaluates to
    false or an invalid argument is given.

Document préparé par Alain Boudreault, pour les étudiants du cours 420-1C5, révision 2