Guide de base pour la création de scripts sous SHEL et Python

Publié le : 13/02/24
Modifié le : 24/06/24 à 17h17

Introduction au script Shell


Un script shell est un fichier texte, qui contient une suite de commandes shell qui sont exécuté par un interpréteur. Ces scripts permettent d'invoquer directement des commandes systèmes.
Les commandes shell retournent toujours :

  • Code de bon fonctionnement : 0/Aucune erreur; 1/ou autre une erreur;

Shell est un langage de commande et d'échange avec le système.
Quelques shell :

  • Bash pour Bourne Again Shell
  • csh, tcsh
  • ksh
Script de base
Shell
Python

Pour exécuter un script shell il faut lui donner les droits d'exécution :
chmod u+x nom_du_script.sh
u : Ajoute les droits d'exécution uniquement pour le propriétaire du fichier.

                                        #!/bin/bash
echo "nous somme le " $(date)
exit 0

                                        

Les variables en shell


Les variables en Shell, ne sont ni déclaré ni typé. On référence une variable avec le symbole $.
Les variables d'environnements sont mise à disposition pour nos scripts :

  • $USER : L'utilisateur actuellement connecté.
  • $PWD : Le répertoire courant.
  • $HOME : Le répertoire home de l'utilisateur connecté.
Exemple de script shell qui utilise les variales d'environnements
Shell
Python
                                        #!/bin/bash
var_bjr1="bonjour les amis"
var_bjr2=bonjour
formatted_date=$(date +"%Y%m%d%H%M%S")

echo $var_bjr1
echo $var_bjr2
echo $formatted_date

echo L\'utilisateur connecté est : $USER
echo Répertoire d\'exécution est : $PWD
echo Répertoire de l\'utilisateur est : $HOME
exit 0

                                        
Variable Signification
$? Valeur de sortie de la dernière commande
$0 Nom du script
$n Nième argument passé au script
$# Nombre d'arguments reçus à partir de $1
$* Liste des arguments à partir de $1
$$ PID du processus courant
Shell
                                        #!/bin/bash
echo "Ce script se nomme : " $0
echo "Le premier argument passé est :" $1
./script_1.sh
echo "La dernière commande a donné :" $? # 0 si aucune erreur
exit 0

                                        
Concaténation
                            #!/bin/bash
nom="ILLOURMANE"
prenom="Mahmoud"
nom_prenom="${nom}-${prenom}"
echo $nom_prenom

exit 0

                            

Les conditionnelles


Les conditionnelles en shell scripting permettent d'exécuter des commandes ou des blocs de commandes basées sur des conditions spécifiques. Voici quelques exemples pour illustrer comment utiliser les conditionnelles en shell.

emoji_objects

-Comparaison de chaînes de caractères :
Pour les chaînes de caractères on doit utiliser : = ou == ou != ...
- Comparaison de numériques :
Pour comparer des numériques on doit utiliser les opérateurs : -eq, -lt, -gt ...

if fi
if elif else fi
case in

Tester si un fichier existe

                                        #!/bin/bash
nombre1=5
nombre2=10
if [ $nombre1 -lt $nombre2 ]; then
    echo "$nombre1 est inférieur à $nombre2."
else
    echo "$nombre1 n'est pas inférieur à $nombre2."
fi

                                        

Les Boucles


for
while
untile
                                        #!/bin/bash

for ville in "Paris" "Lyon" "Marseille"; do
    echo "Je visite $ville."
done

                                        
                                        #!/bin/bash

for i in 1 2 3 4 5; do
    echo "Nombre: $i"
done

                                        
emoji_objects

L'utilisation de ; est nécessaire dans les deux exemples.

Opérations sur les variables


Opérations mathématiques
Incrémentation
Addition 1
Addition 2
                                        #!/bin/bash
var1=0
echo $var1
let var1++
echo $var1

                                        
Exécution
                                        mahmoud@Mahmoud:script_bash$ ./script_4.sh
    0
    1

                                        
emoji_objects

Il faut faire attention au espaces var5=45 et non var5 = 45

Opérations sur les tableaux


On Shell, les tableaux sont assez flexibles ils peuvent être considéré comme des listes, des tableaux..
On peut ajouter des élements à la volé.

Shell
Déclaration global
                                        #!/bin/bash
tab=("Mahmoud" "ILLOURMANE")
exit 0

                                        
Déclaration individuelle
                                        #!/bin/bash
tab1[0] = "Mahmoud"
tab1[1] = "ILLOURMANE"
exit 0

                                        
Les tableau associatif

les tableaux associatifs existent dans le shell Bash à partir de la version 4.0

                                        #!/bin/bash

# Déclare un tableau associatif
declare -A tab_vms

# Remplir le tableau associatif
tab_vms["VM1"]="05d4ddb6-090a-8992-a71f-6ea54ee89fef"
tab_vms["VM2"]="55042bd4-9ee0-d1a2-d9ea-55c2eef69683"

# Parcours du tableau associatif
for vm_name in "${!tab_vms[@]}"; do
    # Obtenir l'UUID de la VM en fonction du nom
    vm_uuid=${tab_vms[$vm_name]}

    echo "Traitement de la VM : $vm_name avec l'UUID : $vm_uuid"
done

exit 0

                                        
Affichage
                                        #!/bin/bash
echo ${tab[0]}

# Affichage de tout le tableau
echo ${tab[@]}
exit 0

                                        
Obtenir la taille d'un tableau
                                        #!/bin/bash
tab=("Mahmoud" "ILLOURMANE" 25)
echo ${#tab[*]}

# Stockage de la taille dans une variable
len=${#tab[*]}
echo $len
exit 0

                                        
Parcourir un tableau
                                        #!/bin/bash

# Définition du tableau
tab=("Mahmoud" "ILLOURMANE")
    
for element in "${tab[@]}"; do
    echo "Traitement de l'élément : $element"
done
exit 0

                                        
emoji_objects

- Il ne doit pas y avoir d'espace entre le nom du tableau tab=(
- Il doit y avoir uniquement un espace entre les éléments du tableau.
- Bash ne fera aucune vérification sur les éléments donc si on essaye d'appeler une case qui n'existe pas on aura aucun avertissement.

Tests


Les tests en shell, souvent réalisés avec la commande test ou les crochets [ ], sont utilisés pour évaluer des expressions conditionnelles dans les scripts Shell. Voici un résumé des points clés :

  • Test de Conditions : Les tests permettent de vérifier différentes conditions telles que l'existence d'un fichier, la comparaison de chaînes de caractères ou de nombres, etc.
  • Commande test : La commande test est utilisée pour effectuer des tests conditionnels. Elle prend en entrée une expression conditionnelle et renvoie 0 (vrai) si la condition est vraie et 1 (faux) sinon.
  • Crochets [ ] : Les crochets [ ] sont une alternative à la commande test. Ils fonctionnent de la même manière mais nécessitent un espace avant et après les crochets. Ils sont souvent utilisés pour améliorer la lisibilité du code.
Opérateurs de Comparaison :

Les tests permettent d'utiliser différents opérateurs de comparaison tels que :

  • -eq : égal =
  • -ne : différent !=
  • -lt : inférieur <
  • -gt : supérieur >
  • -le : inférieur ou égal <=
  • -ge : supérieur ou égal >=
Tests sur les fichiers
  • -e : Le fichier existe
  • -f : C'est un fichier
  • -d : C'est un répertoire
  • -r | -w | -x : Propriété du fichier
  • -s : Non vide
  • f1 -nt f2 : Plus récent que
  • f1 -ot f2 : Plus vieux que
Exemples
-e
-f
-d
-(r,w,x)
-s
-nt
-ot

Ce script vérifie que un nom de fichier qu'on passe en paramètre est bien présent dans le répertoire courant où se trouve le script.

                                        #!/bin/bash

# Affiche l'usage du script si aucun argument n'est fourni
if [ $# -eq 0 ]; then
    echo "Usage: $0 "
    exit 1
fi
    
# Définit le nom du fichier à partir du premier argument
file="$1"
    
# Tente d'obtenir le chemin absolu du répertoire contenant le script
# et vérifie si l'opération réussit
script_dir=$(dirname "$(readlink -f "$0")") || { echo "Erreur lors de la résolution du chemin du script"; exit 1; }
    
# Construit le chemin complet vers le fichier ciblé
file_path="${script_dir}/${file}"
    
# Vérifie l'existence du fichier et affiche un message approprié
if [ -e "$file_path" ]; then
    echo "Le fichier ${file} existe dans le même répertoire que le script."
else
    echo "Le fichier ${file} n'existe pas dans le même répertoire que le script."
fi

                                        

Exécution

                                        mahmoud@Mahmoud:script_bash$ ./script_6_file_exist.sh
    Usage: ./script_6_file_exist.sh <nom_du_fichier> 
mahmoud@Mahmoud:script_bash$ ./script_6_file_exist.sh script_1.sh
    Le fichier script_1.sh existe dans le même répertoire que le script.
mahmoud@Mahmoud:script_bash$ ./script_6_file_exist.sh script_5.sh
    Le fichier script_5.sh n'existe pas dans le même répertoire que le script.

                                        
Expressions Logiques
  • exp1 -a exp2 : ET
  • exp1 -o exp2 : OU
  • !exp : Négation
Exemples
-a
-o
!

Ce script vérifie si deux chaînes fournies sont non vides.

                                        #!/bin/bash

# Vérifie si les deux chaînes sont non vides
if [ -n "$1" -a -n "$2" ]; then
    echo "Les deux chaînes sont non vides."
else
    echo "Au moins une des deux chaînes est vide."
fi

                                        
Tests sur les chaînes de caractères
  • -z Chaîne vide
  • -n Chaîne non vide
  • != Différentes
  • = Identiques
Exemples
-z
-n
!=
=

Ce script vérifie si la chaîne de caractères fournie est vide.

                                        #!/bin/bash

# Vérifie si la chaîne de caractères est vide
if [ -z "$1" ]; then
    echo "La chaîne est vide."
else
    echo "La chaîne n'est pas vide."
fi

                                        

La Gestion des Flux


emoji_objects

- Entrée standard (stdin) : C'est le flux par lequel le processus reçoit des données. Son descripteur de fichier est 0.
- Sortie standard (stdout) : C'est le flux par lequel le processus envoie des données vers l'écran par défaut, par exemple le terminal. Son descripteur de fichier est 1.
- Sortie d'erreur standard (stderr) : C'est le flux utilisé par le processus pour envoyer des messages d'erreur. Son descripteur de fichier est 2.
Exemples d'utilisation dans la section : Redirection du flux d'erreur

Redirection d'entrée standard depuis un fichier

Lire le contenu d'un fichier et l'utiliser comme entrée pour une commande.
Dans cet exemple, je redirige le contenu du fichier noms.txt vers la commande sort, qui va trier les lignes du fichier.

                            #!/bin/bash
file="$1"
sort < "$file"

                            
Redirection du flux standard

Écrire la sortie d'une commande dans un fichier.

                            #!/bin/bash
echo "Bonjour le monde" > sortie.txt

                            
Redirection du flux d'erreur

Dans cet exemple, je cherche le mot "recherche" dans un fichier qui n'existe pas, et je redirige les erreurs générées dans le fichier erreurs.txt.

                            #!/bin/bash
grep "recherche" fichier_inexistant.txt 2> erreurs.txt

                            
Redirection de la sortie d'erreur vers la sortie standard

Rediriger à la fois la sortie standard et la sortie d'erreur vers le même fichier.

                            #!/bin/bash
commande 2>&1 > sortie_et_erreurs.txt

                            
emoji_objects

Ici, je redirige la sortie d'erreur (2>) sur la sortie standard (&1), puis je redirige cette dernière dans un fichier
(> sortie_et_erreurs.txt).

Redirection dans un fichier (écriture et création du fichier)

Créer un fichier ou écraser le contenu s'il existe déjà, avec la sortie d'une commande.

                            #!/bin/bash
echo "Je crée ou j'écrase un fichier" > nouveau_fichier.txt

                            
Ajout à un fichier

Ajouter la sortie d'une commande à la fin d'un fichier existant.

                            #!/bin/bash
echo "J'ajoute cette ligne à la fin du fichier" >> fichier_existant.txt

                            
Ecriture dans un fichier

Dans cet exemple, je commence par récupérer la date et l'heure actuelles avec la commande date que je stocke dans la variable date_now. Ensuite, je crée un fichier date_info.txt où je vais écrire la date et l'heure. Avec l'opérateur > je crée le fichier ou j'écrase son contenu s'il existe déjà. Avec l'opérateur >>, je vais ajouter la liste des fichiers et dossiers du répertoire courant à la suite dans le fichier sans écraser le contenu précédent.

                            #!/bin/bash

# Je stocke la commande `date` dans la variable date_now
date_now=$(date)
    
# Je crée un fichier nommé 'date_info.txt'
# Je vais écrire la date et l'heure actuelles dedans
echo "La date et l'heure actuelles sont : $date_now" > date_info.txt
    
# Ensuite, je vais lister le contenu du répertoire courant et ajouter cette liste au même fichier
echo "Liste des fichiers et dossiers:" >> date_info.txt
ls >> date_info.txt        

                            
                            #!/bin/bash

list=$(ls)
for element in $list
do
    echo $element >> ./toto.txt
done   

                            

                            #!/bin/bash

list=$(cat toto.txt)
for element in $list
do
    echo $element >> ./nouveau_toto.txt
done

                            
Lecture d'un fichier

Dans cette exemple, je vais lire le contenu d'un fichier ligne par ligne et afficher chaque ligne avec un numéro précédant le texte.

                            #!/bin/bash

filename='date_info.txt'
i=1
while read line; do
    # Affichage du numéro de ligne et du contenu de la ligne
    echo "Ligne $i: $line"
    i=$((i + 1))
done < $filename    

                            

                            #!/bin/bash

cpt=1
while read LINE
do
    echo $cpt ": " $LINE
    let cpt++
done < toto.txt

                            

Lecture de l'entrée clavier


                            #!/bin/bash

echo "Entrez votre nom, prénom et âge :"
read nom prenom age
echo "Je m'appelle $prenom $nom et j'ai $age ans."

                            
Exécution
                            mahmoud@Mahmoud:script_bash$ ./script_19_read.sh
    Entrez votre nom, prénom et âge :
    illourmane mahmoud 25
    Je m'appelle mahmoud illourmane et j'ai 25 ans.

                            
Configuration d'un conteneur Docker
                            #!/bin/bash

echo "Entrez le nom de l'image, le tag et le nom du conteneur :"
read image tag nom_conteneur
echo "Je lance un conteneur Docker avec l'image $image:$tag appelé $nom_conteneur."
docker run --name $nom_conteneur $image:$tag

                            
Exécution
                            mahmoud@Mahmoud:script_bash$ ./script_20_docker.sh
    Entrez le nom de l'image, le tag et le nom du conteneur :
    nginx lastest mon-nginx
    Je lance un conteneur Docker avec l'image nginx:lastest appelé mon-nginx.

                            

Les fonctions


En shell script, il n'est pas requis de spécifier explicitement les paramètres lors de la déclaration d'une fonction. Les fonctions ont la capacité d'accéder aux variables globales ou d'utiliser les arguments passés directement via des paramètres positionnels.
De plus, l'appel d'une fonction se fait sans parenthèses, se distinguant ainsi des syntaxes des langages de programmation tels que Python ou Java. On appelle simplement la fonction par son nom suivi de ses arguments.

Fonction sans paramètre
                            #!/bin/bash

# Déclaration de la fonction
fun_1() {
    echo L\'utilisateur qui lance la fonction est : $USER
}

# Appel de la fonction
fun_1

                            
Fonction avec paramètres

Dans cet exemple, la fonction attend deux paramètres envoyés en argument pour additionner deux chiffres.

                            #!/bin/bash

fun_2() {
    let result=$1+$2
    result2=$(($1 + $2))
    echo 1-Le résultat de l\'addition est : $result
    echo 2-Le résultat de l\'addition est : $result2
}

fun_2 $1 $2

                            
Fonction avec paramètre entré au clavier
                            #!/bin/bash

fun_3() {
    echo "En Fahrenheit, cela fait : $((($1 * 9/5) + 32))°F"
}
    
echo "Entrez la température en Celsius :"
read celsius
    
fun_3 $celsius

                            
Une fonction qui vérifie si un fichier existe
                            #!/bin/bash

fun_4() {
    if [[ -e $1 ]]; then
        echo "Le fichier $1 existe."
    else
        echo "Le fichier $1 n'existe pas."
    fi
}
    
echo "Entrez le nom du fichier à vérifier :"
read nomFichier
    
fun_4 $nomFichier

                            

Manipuler des chaînes de caractères


En shell il est possible de manipuler les chaînes de caractères pour extraires des informations, modifier un symbol délimiteur etc.
Cela simplifier les programmes, et évite l'utilisation d'expression régulière.
Attention dans le cas des séparateurs, les modifications sont persistantes.

Extraction d'une chaîne/partie d'une Chaîne

la syntaxe ${c:p:n} permet d'extraire une chaîne ou une partie d'une chaîne facilement.

emoji_objects

${ chaine : position : nombre_de_caractères }
- Il n'y a pas d'espace entre les : et {}.
- chaine : La chaîne dont on souhaite extraire les informations.
- position : La position (à partir de 0) à laquelle on souhaite débuter.
- nombre_de_caractères : Le nombre de caractères qu'on souhaite extraire.
- Cela ignore les espaces mais ils sont comptabilisé.

                                #!/bin/bash

chaine="Bonjour à tous"
chaine2=${chaine:7:3}
chaine3=${chaine:10:4}
echo $chaine
echo $chaine2
echo $chaine3

                                
Exécution
                                mahmoud@Mahmoud:script_bash$ ./script_1_chaine_extrac.sh
    Bonjour à tous
    à
    tous

                                

Trancature d'une chaîne de caractères

la syntaxe ${ c : n } permet de tranquer une chaîne de caractères.

                                #!/bin/bash

chaine="Bonjour à tous"
chaine2=${chaine:10}
echo $chaine
echo $chaine2

                                
Exécution
                                mahmoud@Mahmoud:script_bash$  ./script_2_trancature.sh
    Bonjour à tous
    tous

                                

Séparateur IFS


En programmation shell, la variable IFS, pour 'Internal Field Separator', est fondamentale pour lire et interpréter correctement les données. Elle définit le séparateur de champ à utiliser lors de la division d'une ligne de texte en mots.
Par défaut, elle inclut le retour à la ligne (newline), l'espace (space) et la tabulation (tab), mais je peux la redéfinir pour changer le comportement de split implicite selon mes besoins spécifiques.

emoji_objects

- Modification persistante

Redéfinition de l'IFS espace

                                #!/bin/bash

oldIFS=$IFS
chaine="Bonjour;Hello;Hola"
echo $chaine
# Redéfinition de l'IFS
IFS=';'
echo $chaine
# Remise à la normale
IFS=$oldIFS
echo $chaine

                                
Exécution
                                mahmoud@Mahmoud:script_bash$  ./script_1_ifs.sh
    Bonjour;Hello;Hola
    Bonjour Hello Hola
    Bonjour;Hello;Hola

                                

Redéfinition de l'IFS pour un fichier

                                #!/bin/bash

# Définition de la fonction d'affichage
afficher_infos() {
    local IFS_SAVE=$IFS  # Sauvegarde de l'IFS actuel
    IFS=';'              # Définition de la virgule comme nouveau IFS
    for var in $1 
    do
        echo $var
    done
    IFS=$IFS_SAVE
    echo "======="
}
    
# Lecture du fichier ligne par ligne
while read LINE 
do
    afficher_infos $LINE
done < "fic.txt"

                                
Exécution
                                # Fichier fic.txt
Hello;Bonjour;Hola
Azerty;Qwerty
Python;Shell

mahmoud@Mahmoud:script_bash$   ./script_2_ifs.sh
    Hello
    Bonjour
    Hola
    =======
    Azerty
    Qwerty
    =======
    Python
    Schell
    =======

                                

Appel système


Exécution d'un appel système pour installer un package :

                            #!/bin/bash

# Exécute la commande apt install
apt install <package_name>
    
# Vérifie le code de retour de la commande
if [ $? -ne 0 ]; then
    # En cas d'erreur, affiche un message d'erreur et quitte avec un code de sortie non nul
    echo "Erreur lors de l'installation du paquet."
    exit 1
else
    # Si la commande s'est exécutée avec succès, affiche un message de réussite
    echo "Installation du paquet terminée avec succès."
fi

                            
content_copy

Pour gérer les erreurs on peux utiliser un mélange de redirections de sortie et de structures de contrôle pour afficher les messages d'erreur :

                            #!/bin/bash

# Fonction pour afficher les messages d'erreur
print_error() {
    echo "Erreur: $1" >&2
}
    
# Exécute la commande apt install
if ! output=$(apt install  2>&1); then
    print_error "Impossible d'installer le paquet."
    print_error "$output"
    exit 1
fi
    
# Si la commande s'est exécutée avec succès, affiche un message de réussite
echo "Installation du paquet terminée avec succès."        

                            
content_copy
Extraire une information

Dans cet exemple, nous allons voir comment extraire une information contenu dans une sortie d'appel système :

                            xe snapshot-list params=uuid
    uuid ( RO)    : b4568bdb-0aad-f63f-5a15-610421a65614
    uuid ( RO)    : e729e7a2-391f-6312-7d28-5ea5006615a7

                            

Le but ici est d'extraire uniquement la valeur de l'uuid. Pour cela nous allons utiliser awk, cette commande permet d'extraire des champs spécifiques à partir de la sortie de commande en spécifiant le délimiteur (par défaut, c'est l'espace ou la tabulation) et le numéro de champ.
En partant de la sortie précédente, on voit que chaque ligne contient un préfixe uuid ( RO) : suivi de l'UUID.
Dans ce cas, le préfixe uuid ( RO) : compte comme quatre champs lorsqu'on considère l'espace comme délimiteur :
- Champ 1 : uuid
- Champ 2 : (
- Champ 3 : RO)
- Champ 4 : ":"
L'UUID lui-même commence après ces quatre champs et est donc considéré comme le cinquième champ.
En utilisant awk, on utilisant on peut spécifier quel champ on souhaite extraire :

                            xe snapshot-list params=uuid | awk '/uuid \( RO\)/ {print $5}'

                            
emoji_objects

Nous n'avons pas spécifié les caractères ":" car :
L'utilisation de /uuid \( RO\)/ emploie une expression régulière pour rechercher les lignes qui contiennent uuid ( RO) :.
Entre uuid et (, il y a un espace, et de même entre ( et RO). Cependant, entre RO) et :, il y a plus d'espace. Nous pouvons donc nous contenter de chercher le motif sans les :, et cela fonctionnera.

Commandes avec interactions

Lorsqu'une commande attend une réponse yes ou autre chose de notre part, nous pouvons l'automatiser en utilisant un pipe | avec les commandes echo ou yes. Cela nous permet de gérer les confirmations requises dans un script. Voici comment scripter ce genre d'appel :

Méthode 1 : Utiliser echo avec un pipe (|)

On utilise echo pour envoyer une réponse (yes ou no) à une commande qui attend une confirmation.
Exemple : Pour une commande qui attend yes pour continuer, on peut utiliser echo avec un pipe (|) :

                            #!/bin/bash

echo "yes" | xe template-uninstall template-uuid=$tmp_uuid_template 

exit 0

                            
content_copy
Méthode 2 : Utiliser yes

La commande yes répète en continu une réponse (yes ou no) et la pipe à la commande.
Exemple : Pour envoyer en continu yes à une commande :

                            #!/bin/bash

yes | xe template-uninstall template-uuid=$tmp_uuid_template 

exit 0

                            
content_copy

Nous pouvons personnaliser la réponse répétée par yes en ajoutant l'argument souhaité :

                            #!/bin/bash

yes "yes" | xe template-uninstall template-uuid=$tmp_uuid_template 

exit 0

                            
content_copy
emoji_objects

Pour certaines commandes, l'approche consistant à utiliser (yes) peut ne pas fonctionner.

Introduction aux scripts Python


En Python, l'écriture est simplifiée tout en exploitant judicieusement les modules, en gardant à l'esprit que Python est un langage interprété.
Il est important de faire attention aux différences entre Python 2.x et 3.x, notamment en termes d'évolution de la bibliothèque standard et du codage en UTF-8, et de comprendre que dans ce langage, tout est traité comme un objet, y compris les fonctions comme `print`.

Scripting Système en Python

Python pour le scripting système permet l'exploitation des éléments système, offre un codage spécifique pour la gestion de ces éléments, et utilise des bibliothèques telles que sys pour interagir avec l'interpréteur, os pour accéder aux fonctionnalités du système d'exploitation et subprocess pour exécuter des processus externes.

                            #!/usr/bin/env python3
import sys
    
print(f"Nombre d'arguments {len(sys.argv)}")
print(sys.argv[0])
exit(0)

                            
emoji_objects

- #!/usr/bin/env python3 : Cette ligne est un shebang, qui indique au système d'exploitation quel interpréteur utiliser pour exécuter le script.
- import sys : Cette ligne importe le module sys, qui fournit un accès à certaines variables utilisées ou maintenues par l'interpréteur Python, ainsi qu'à des fonctionnalités spécifiques au système.
- exit(0) : Fin de script 0 : OK, X : ERREUR.

Exécution
                            mahmoud@Mahmoud:/script_python$ python3 s_1.py
    Nombre d'arguments 1
    s_1.py

mahmoud@Mahmoud:/script_python$ python3 s_1.py test
    Nombre d'arguments 2
    s_1.py

                            

Obtenir des informations sur la plateforme d'exécution du script.

                            #!/usr/bin/env python3
import sys, os
    
print(f"Plate-forme d'exécution :  {sys.platform}, {os.name}")
exit(0)

                            
Exécution
                            mahmoud@Mahmoud:/script_python$ python3 s_2 .py
    Plate-forme d'exécution :  linux, posix

                            

Les Flux Standards

Les flux standards (stdin, stdout, stderr) sont définis dans le package sys.
Les opérations d'entrées/sorties sont définis avec read(sur stdin)/write.

  Ecritures sur les flux stdout, stderr

                            #!/usr/bin/env python3
import sys

sys.stdout.write("J'écris sur la sortie standard \n")
sys.stderr.write("J'écris sur la sortie d'erreur \n")
exit(0)

                            
Exécution
                            mahmoud@Mahmoud:/script_python$ python3 s_3 .py
    J'écris sur la sortie standard
    J'écris sur la sortie d'erreur

                            

  Lecture sur le flux stdin

                            #!/usr/bin/env python3
import sys
    
print("Ecrivez plusieurs noms de fruits non à la suite..")
print("Quand vous aurez fini cliquez sur CTRL+D")
fruits = sys.stdin.readlines()
    
print("Vous avez saisi les fruits suivants:")
for fruit in fruits:
    print(fruit.strip())
        
# Lecture plus simple :

fruit = input("Entrez un nom de fruit : ")
print(f"Vous avez saisi le fruit : {fruit}")

exit(0)

                            
emoji_objects

- strip : cela supprime les espaces blancs éventuels.

Exécution
                            mahmoud@Mahmoud:/script_python$ python3 s_4.py
    Ecrivez plusieurs noms de fruits non à la suite..
    Quand vous aurez fini cliquer sur CTRL+D
    Pomme
    Orange
    Bannane
    Vous avez saisi les fruits suivants:
    Pomme
    Orange
    Bannane

    Entrez un nom de fruit : Orange
    Vous avez saisi le fruit : Orange

                            

Les Fichiers

  Ouverture d'un fichier

En Python il existe la commande open pour ouvrir et manipuler les fichiers. La commande open prend en paramètre le nom du fichier, le flag d'accès à ce fichier [r,w,a(append)]. On termine notre opération par la fermeture du fichier avec fd.close()

                                #!/usr/bin/env python3
import sys
    
# Vérification de l'argument
if len(sys.argv) <= 1:
    sys.stderr.write("Vous devez indiquer un nom de fichier en argument.\n")
    exit(1)
    
file = sys.argv[1]      # Récupération du nom du fichier
fd = open(file, 'r')    # Ouverture du fichier en mode lecture
content = fd.read()     # Lecture du fichier
print(content)          # Affichage du contenu du fichier
    
fd.close()              # Fermeture du fichier
exit(0)

                                
Exécution
                                mahmoud@Mahmoud:/script_python$  python3 s_6.py file.txt
    Orange
    Bannane
    Pomme
    Clémentine
    Mandarine

                                
  Lectures d'un fichier
emoji_objects

Modes de lecture :
- read(taille) : Si on indique aucune taille il lira tout le fichier.
- readlines : Cette méthode lit toutes les lignes d'un fichier et les retourne sous forme de liste.
- readline : Est utilisée pour lire une seule ligne à partir d'un fichier. À chaque appel de readline(), elle lit la ligne suivante.

readlines
readline
readline Nième ligne

Exemple d'utilisation de readlines :

                                            #!/usr/bin/env python3
import sys
    
file = sys.argv[1]     
fd = open(file, 'r')    
content = fd.readlines()    
print(content)         
    
fd.close()              
exit(0)

                                            
Exécution
                                            mahmoud@Mahmoud:/script_python$ python3 s_7.py file.txt
    ['Orange\n', 'Bannane\n', 'Pomme\n', 'Clémentine\n', 'Mandarine']

                                            
  Ecriture dans un fichier
emoji_objects

Modes d'écritures :
- write : Écriture d'une seule ligne dans un fichier
- writelines(lignes) : Écriture de plusieurs lignes dans un fichier. lignes est une liste.
- mode 'a' : Ajout de contenu à un fichier existant

write
writelines
write 'a'
emoji_objects

- En mode 'w', si le fichier existe déjà son contenu sera écrasé.

                                            #!/usr/bin/env python3

with open('fichier.txt', 'w') as fichier:
    octets = fichier.write("Ceci est une ligne dans un fichier.\n")
    print(f"{octets} octets écris.")
    exit(0)

                                            
Exécution
                                            mahmoud@Mahmoud:/script_python$ python3 s_10.py
    36 octets écris.

                                            

Le Système de Fichier

Python nous permet de créer facilement des scripts qui intéragissent avec le système de fichier tel que les répertoires.
On peut en trautre :

  • Modifier les propriétés des fichiers
  • Obtention du répertoire racine, courant...
  • Création de répertoire, suppression...

Nous utiliserons le package : os.path, ainsi que les bibliothèques : shutil filecmp

  Opérations sur les répertoires

Nous allons voir des scripts qui manipule les répertoires où s'exécute le script.

os.chdir
os.listdir
os.mkdir

Changement de répertoire :

                                        #!/usr/bin/env python3
import os

# Changement du répertoire de travail pour le répertoire parent du répertoire courant.
os.chdir("..")          
exit(0)

                                        
  shutil

Ce premier script démontrera comment utiliser shutil pour copier un fichier d'un emplacement à un autre.

                                #!/usr/bin/env python3
import shutil
import os

source_file = 'source.txt'      # Nom du fichier source à copier
destination_dir = 'backup/'     # Dossier de destination

# Je vérifie si le répertoire de destination existe, sinon je le crée
if not os.path.exists(destination_dir):
    os.makedirs(destination_dir)

# Chemin complet du fichier de destination
destination_file = os.path.join(destination_dir, os.path.basename(source_file))

# Je copie le fichier seulement s'il n'existe pas déjà dans le répertoire de destination
if not os.path.exists(destination_file):
    shutil.copy(source_file, destination_dir)
    print(f"Le fichier {source_file} a été copié vers {destination_dir}")
else:
    print(f"Le fichier {source_file} existe déjà dans le répertoire de destination.")

exit(0)

                                
Exécution
                                mahmoud@Mahmoud:/script_python$ python3 s_14_rep.py s_1.py
Le fichier s_1.py a été copié vers backup/

                                
  filecmp

Cet exemple permet de faire une comparaison entre deux fichiers :

                                #!/usr/bin/env python3
import filecmp
import sys

fichier_1 = sys.argv[1]
fichier_2 = sys.argv[2]
res = filecmp.cmp(fichier_1, fichier_2)
if res:
    print("Les fichiers sont identiques.")
else:
    print("Les fichiers ne sont pas identiques.")
exit(0)

                                
Exécution
                                mahmoud@Mahmoud:/script_python$ python3 s_16_rep.py s_9.py s_10.py
    Les fichiers ne sont pas identiques.
mahmoud@Mahmoud:/script_python$ python3 s_16_rep.py s_9.py s_9.py
    Les fichiers sont identiques.

                                
  os.path.isfile

Cet exemple permet de vérifier que un nom de fichier passé en argument est vraiment un fichier :

                                #!/usr/bin/env python3
import sys
import os
    
fichier = sys.argv[1]
    
if os.path.isfile(fichier):
    print("C'est un fichier.")
else:
    print("Ce n'est pas un fichier.")
    
exit(0)

                                
Exécution
                                mahmoud@Mahmoud:/script_python$ python3 s_17_rep.py s_9.py
    C'est un fichier.
mahmoud@Mahmoud:/script_python$ python3 s_16_rep.py backup/
    Ce n'est pas un fichier.

                                
  os.path.isdir

Cet exemple permet de vérifier que le nom reçu en argument est bien un répertoire :

                                #!/usr/bin/env python3
import sys
import os
        
rep = sys.argv[1]
    
if os.path.isdir(rep):
    print("C'est un répertoire.")
else:
    print("Ce n'est pas un répertoire.")
        
exit(0)

                                
Exécution
                                mahmoud@Mahmoud:/script_python$ python3 s_18_rep.py backup/
    C'est un répertoire.
mahmoud@Mahmoud:/script_python$ python3 s_18_rep.py s_1.py
    Ce n'est pas un répertoire.

                                

Exécution de commandes système

Python dispose de bibliothèque et package pour exécuter des commandes système depuis un script.

  Subprocess
  • Subprocess : Le module subprocess de Python permet d'exécuter des commandes système et de contrôler des processus externes depuis un script Python.
  • subprocess.Popen : est une fonction du module subprocess qui lance un nouveau processus externe avec les paramètres spécifiés et permet au script Python de communiquer avec ce processus et de contrôler son exécution.
  • subprocess.run : est une autre fonction du module subprocess qui est souvent recommandée pour remplacer subprocess.Popen dans les cas simples où l'on souhaite juste exécuter une commande et attendre sa fin. Elle offre une interface plus simple et plus sûre pour exécuter des commandes système à partir de Python.
  Subprocess.Popen
Sans sudo
Avec sudo

Exemple de script Python qui exécute la commande ls -al dans un processus externe.

                                            #!/usr/bin/env python3
import subprocess
    
p = subprocess.Popen(["ls", "-al"], stdout=subprocess.PIPE)
print(p.communicate())
exit(0)              

                                            
emoji_objects

- Ligne 3 : Cette ligne exécute la commande ls -al dans un processus externe et récupère sa sortie standard. Le résultat est stocké dans l'objet p.
- print(p.communicate()) : Cette ligne imprime la sortie du processus créé précédemment à l'aide. Cette méthode renvoie un tuple contenant la sortie standard et la sortie d'erreur du processus. Dans ce cas, seule la sortie standard est imprimée.

  Subprocess.run

Exemple de script Python qui exécute la commande ls -al dans un processus externe.

                                #!/usr/bin/env python3
import subprocess

# J'exécute la commande 'ls -al' et je capture la sortie
result = subprocess.run(['ls', '-al'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)

# J'affiche la sortie de la commande
if result.returncode == 0:
    print("Sortie de la commande 'ls':\n", result.stdout)
else:
    print("Erreur lors de l'exécution de 'ls':\n", result.stderr)
    exit(1)
exit(0)

                                
emoji_objects

- text=True : Ce paramètre sert à indiquer que les données échangées entre le processus principal et le processus enfant doivent être traitées comme des chaînes de caractères (texte), plutôt que des bytes.

  Subprocess.run avec shell=True

L'option shell=True dans subprocess.run() permet d'exécuter la commande à travers un shell interpréteur, ce qui te donne accès aux fonctionnalités du shell comme les tubages et les redirections.
Cependant, cela peut être moins sûr à cause du risque d'injection de commandes si la commande est construite à partir d'entrées utilisateur. Sans shell=True, la commande est exécutée directement sans interpréteur de shell, ce qui est plus sûr et légèrement plus performant pour des commandes simples.
L'option check=True est utilisée en conjonction avec shell=True dans un appel à subprocess.run() fait que Python lèvera une exception subprocess.CalledProcessError si la commande exécutée renvoie un code de sortie non nul

                                #!/usr/bin/env python3
import subprocess

try:
    # Lève une exception si la commande échoue
    subprocess.run('ls -l', shell=True, check=True)
except subprocess.CalledProcessError as e:
    print(f"Erreur: La commande a échoué avec le code de retour {e.returncode}")
    exit(1)

exit(0)

                                
emoji_objects

Dans cet exemple, shell=True permet d'utiliser les fonctionnalités du shell pour exécuter la commande, et check=True assure que toute erreur dans l'exécution de la commande provoque l'arrêt de ton script en levant une exception.

Expréssions régulières

Le module re en Python est spécialement dédié à la manipulation des expressions régulières, mais il propose plusieurs fonctionnalités qui dépassent le simple usage des expressions régulières en elles-mêmes. Voici quelques-unes des caractéristiques et fonctionnalités supplémentaires du module re :

- Compilation des expressions régulières : Les expressions régulières peuvent être compilées en objets de motif à l'aide de re.compile().

- Groupes de capture : Le module re permet de définir des groupes de capture à l'intérieur des expressions régulières, facilitant ainsi la récupération de parties spécifiques d'une chaîne correspondante pour un traitement ultérieur.

- Flags : Le module re permet l'utilisation de flags qui modifient certains aspects de la correspondance des expressions régulières, tels que la sensibilité à la casse re.IGNORECASE, le mode multiligne re.MULTILINE, et le mode point-tout re.DOTALL.

- Méthodes de correspondance multiples : En plus de re.search(), qui recherche une correspondance n'importe où dans la chaîne.

- re.match() : recherche qu'au début de la chaîne.

- re.fullmatch() : exige que toute la chaîne corresponde.

- re.findall() et re.finditer() : retournent toutes les correspondances non chevauchantes sous forme de listes ou d'itérateurs.

- Substitution re.sub() re.subn() : permettent de remplacer des textes dans une chaîne qui correspondent à une expression régulière.

- Division de chaîne re.split() : il est possible de diviser une chaîne en utilisant une expression régulière comme délimiteur, offrant ainsi une méthode plus puissante que la méthode split() intégrée des chaînes.

- Échappement de caractères re.escape() : peut être utilisé pour échapper tous les caractères spéciaux dans une chaîne, afin qu'ils soient traités littéralement dans les expressions régulières.

Des exemples sont illustrés ci-dessous :

re.compile
groupe re.compile
Flags
correspondance multiples
Substitution
Division de chaîne
Échappement de caractères

Ce script compile d'abord une expression régulière qui recherche des séquences de chiffres (\d+).

                                        #!/usr/bin/env python3
import re

# Compilation de l'expression régulière
pattern = re.compile(r'\d+')  # Trouver des séquences de chiffres
    
# Utilisation de l'objet de motif compilé pour rechercher dans une chaîne
match = pattern.search('Le numéro est 12345')
if match:
    print(match.group())  # Affiche '12345'
exit(0)

                                        
Vérification du nombre d'argument

On doit toujours vérifier qu'on reçoit bien le bon nombres d'arguments :

Shell
Python
                                        #!/bin/bash

# Vérification du nombre de paramètres
if [ $# -eq 0 ]; then
    echo "Je dois utiliser ce script comme suit: $0 <votre_prenom>"
    exit 1
fi
    
prenom=$1

echo "Bonjour "$prenom" nous somme le :" $(date)
exit 0

                                        
content_copy
Appel système

Lorsqu'on exécute une commande système, on doit toujours vérifier si elle a bien été exécutée.

Shell
Python
                                        #!/bin/bash

# Exécute la commande apt install par exemple
apt install <package_name>
    
# Vérifie le code de retour de la commande
if [ $? -ne 0 ]; then
    # En cas d'erreur, affiche un message d'erreur et quitte avec un code de sortie non nul
    echo "Erreur lors de l'installation du paquet."
    exit 1
else
    # Si la commande s'est exécutée avec succès, affiche un message de réussite
    echo "Installation du paquet terminée avec succès."
fi

                                        
content_copy
Exécution d'un script en tant que root

Certains scripts doivent être exécutés en tant que root. Il est essentiel de vérifier cela avant de lancer le script :

Shell
Python
                                        #!/bin/bash

# Vérification si le script est lancé avec les privilèges root
if [ "$EUID" -ne 0 ]; then
    echo -e "\e[31mErreur :\e[0m Ce script doit être exécuté en tant que root."
    exit 1
fi

echo "Script lancé en tant que root."

                                        
content_copy