Ce que vous saurez faire
Ce tutoriel vous apprend a ecrire des scripts Bash pour automatiser les taches repetitives de votre PME senegalaise : sauvegardes quotidiennes, deploiements d’applications, rapports automatises envoyes par email, surveillance de services, et traitement de fichiers en masse. Bash est le shell par defaut sur la plupart des serveurs Linux, ce qui rend vos scripts portables entre machines. Vous apprendrez la syntaxe des variables, conditions, boucles, fonctions, et comment gerer les arguments et codes de retour. A la fin de ce guide, vous pourrez ecrire des scripts robustes pour remplacer les operations manuelles couteuses en temps : par exemple, un script de sauvegarde MySQL automatique avec rotation et upload vers un serveur distant, ou un script de deploiement qui met a jour le code, redemarre les services, et envoie une notification Slack en une seule commande.
Etape 1 : Premier script executable
Un script Bash commence toujours par un shebang #!/bin/bash qui indique au systeme quel interpreteur utiliser. Creez votre premier script avec nano ou vim.
nano hello.sh
Contenu du fichier :
#!/bin/bash # Mon premier script echo "Bonjour depuis Dakar" echo "Date : $(date)" echo "Utilisateur : $USER"
Rendez-le executable puis lancez-le :
chmod +x hello.sh ./hello.sh # Bonjour depuis Dakar # Date : Thu Apr 23 14:30:00 GMT 2026 # Utilisateur : admin
Etape 2 : Variables et substitution
Les variables en Bash n’ont pas de type. Affectation sans espace autour du =, utilisation avec $. Les guillemets doubles autorisent la substitution, les simples non.
#!/bin/bash nom="Fatou" age=28 ville="Thies" echo "Employee : $nom, $age ans, $ville" echo 'Pas de substitution : $nom' # Variables systeme utiles echo "Home : $HOME" echo "Path : $PATH" echo "PID courant : $$" # Substitution de commande date_aujourdhui=$(date +%Y-%m-%d) nb_fichiers=$(ls /var/www | wc -l) echo "Aujourd'hui : $date_aujourdhui, $nb_fichiers fichiers"
Etape 3 : Arguments de ligne de commande
Les scripts recoivent des arguments via $1, $2, etc. $# donne le nombre d’arguments, $@ la liste complete, $0 le nom du script.
#!/bin/bash
# backup.sh - Sauvegarder un dossier
if [ $# -lt 2 ]; then
echo "Usage : $0 <source> <destination>"
exit 1
fi
source=$1
destination=$2
echo "Sauvegarde de $source vers $destination"
tar -czf "$destination/backup_$(date +%F).tar.gz" "$source"
echo "Nombre d'arguments recus : $#"
echo "Tous les arguments : $@"
Usage : ./backup.sh /var/www /backups
Etape 4 : Conditions avec if
Les tests utilisent [ ] ou [[ ]]. Respectez les espaces autour des crochets. [[ ]] est plus puissant (regex, comparaison de chaines).
#!/bin/bash
fichier="/etc/nginx/nginx.conf"
if [ -f "$fichier" ]; then
echo "Le fichier existe"
elif [ -d "$fichier" ]; then
echo "C'est un dossier"
else
echo "Introuvable"
fi
# Comparaison de nombres
charge=$(cat /proc/loadavg | awk '{print $1}' | cut -d. -f1)
if [ "$charge" -gt 4 ]; then
echo "Alerte : charge elevee"
fi
# Comparaison de chaines
env="production"
if [[ "$env" == "production" ]]; then
echo "Mode prod"
fi
# Tests courants
# -f fichier existe, -d dossier, -e existe
# -r lisible, -w inscriptible, -x executable
# -z vide, -n non vide
Etape 5 : Boucles for et while
Les boucles automatisent les traitements repetes. for itere sur une liste, while continue tant qu’une condition est vraie.
#!/bin/bash
# Boucle sur une liste
for site in site1.sn site2.sn site3.sn; do
echo "Ping de $site"
ping -c 1 "$site" > /dev/null && echo "OK" || echo "KO"
done
# Boucle sur des fichiers
for log in /var/log/*.log; do
taille=$(du -h "$log" | cut -f1)
echo "$log : $taille"
done
# Boucle numerique
for i in {1..5}; do
echo "Tentative $i"
done
# While avec compteur
compteur=0
while [ $compteur -lt 3 ]; do
echo "Iteration $compteur"
compteur=$((compteur + 1))
done
# Lire un fichier ligne par ligne
while read ligne; do
echo "Ligne : $ligne"
done < /etc/hostname
Etape 6 : Fonctions reutilisables
Les fonctions organisent le code. Declarez avec function nom() ou nom(), appelez par le nom. Les arguments passent par $1, $2, etc.
#!/bin/bash
log_info() {
echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') - $1"
}
log_error() {
echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') - $1" >&2
}
verifier_service() {
local service=$1
if systemctl is-active --quiet "$service"; then
log_info "$service est actif"
return 0
else
log_error "$service est arrete"
return 1
fi
}
# Utilisation
log_info "Debut de la verification"
verifier_service "nginx"
verifier_service "mysql"
log_info "Fin"
Etape 7 : Codes de retour et gestion d’erreurs
Chaque commande retourne un code : 0 = succes, autre = erreur. Utilisez $? pour le recuperer. set -e arrete le script a la premiere erreur, set -u refuse les variables non definies.
#!/bin/bash
set -euo pipefail
# -e : arret sur erreur
# -u : erreur si variable non definie
# -o pipefail : arret si pipe echoue
cp /etc/hosts /tmp/hosts_backup
if [ $? -eq 0 ]; then
echo "Copie reussie"
else
echo "Echec de copie"
exit 1
fi
# Piege de nettoyage automatique
cleanup() {
echo "Nettoyage en cours"
rm -f /tmp/verrou.lock
}
trap cleanup EXIT
# Creer un verrou
touch /tmp/verrou.lock
# Travail ...
# cleanup sera appele automatiquement a la sortie
Etape 8 : Case pour choix multiples
Quand vous avez plusieurs conditions sur une meme variable, case est plus lisible qu’une chaine de if/elif.
#!/bin/bash
# deploy.sh env action
env=$1
action=$2
case "$env" in
dev|development)
serveur="dev.itskillscenter.io"
;;
staging)
serveur="staging.itskillscenter.io"
;;
prod|production)
serveur="itskillscenter.io"
;;
*)
echo "Environnement inconnu : $env"
exit 1
;;
esac
case "$action" in
deploy) echo "Deploiement sur $serveur" ;;
rollback) echo "Rollback sur $serveur" ;;
status) echo "Statut de $serveur" ;;
*) echo "Action invalide" ; exit 1 ;;
esac
Etape 9 : Manipulation de chaines
Bash offre des operations natives sur les chaines : longueur, sous-chaine, remplacement, casse.
#!/bin/bash
chaine="Bonjour Dakar Senegal"
# Longueur
echo "${#chaine}" # 21
# Sous-chaine (position 8, longueur 5)
echo "${chaine:8:5}" # Dakar
# Remplacement
echo "${chaine/Dakar/Thies}" # Bonjour Thies Senegal
echo "${chaine//a/@}" # Bonjour D@k@r Seneg@l
# Casse
echo "${chaine,,}" # tout en minuscules
echo "${chaine^^}" # tout en majuscules
# Valeur par defaut
env="${ENV:-development}" # Si ENV non defini, prend "development"
# Retrait prefixe/suffixe
fichier="rapport_2026_04.csv"
echo "${fichier%.csv}" # rapport_2026_04
echo "${fichier#*_}" # 2026_04.csv
Etape 10 : Redirection et pipes
Rediriger la sortie vers un fichier, separer erreurs et sortie standard, chainer des commandes.
#!/bin/bash
# Redirections de base
echo "Sortie standard" > fichier.txt # Ecrase
echo "Ajout" >> fichier.txt # Ajoute
ls fichier_inexistant 2> erreurs.log # Erreur seulement
ls / 2>&1 > tout.log # Les deux vers tout.log
# /dev/null pour silencer
ping -c 1 google.com > /dev/null 2>&1
# Pipe : sortie d'une commande vers entree d'une autre
ps aux | grep nginx | awk '{print $2}' | head -5
# Here-document
cat <<EOF > config.txt
serveur=itskillscenter.io
port=443
ssl=true
EOF
# Here-string
grep "error" <<< "$log_content"
Etape 11 : Script complet de sauvegarde MySQL
Un cas concret : sauvegarder quotidiennement une base MySQL, compresser, faire une rotation sur 7 jours, et envoyer un rapport.
#!/bin/bash
# backup_mysql.sh
set -euo pipefail
# Configuration
DB_USER="backup"
DB_PASS="motdepasse"
DB_NAME="boutique"
BACKUP_DIR="/backups/mysql"
RETENTION_DAYS=7
EMAIL="admin@pme-dakar.sn"
# Creation du dossier si absent
mkdir -p "$BACKUP_DIR"
# Nom du fichier
DATE=$(date +%Y%m%d_%H%M%S)
FICHIER="$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz"
# Dump et compression
echo "Sauvegarde de $DB_NAME"
if mysqldump -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" | gzip > "$FICHIER"; then
TAILLE=$(du -h "$FICHIER" | cut -f1)
echo "Succes : $FICHIER ($TAILLE)"
else
echo "Echec de sauvegarde" | mail -s "Erreur backup" "$EMAIL"
exit 1
fi
# Rotation
find "$BACKUP_DIR" -name "${DB_NAME}_*.sql.gz" -mtime +$RETENTION_DAYS -delete
echo "Nettoyage des sauvegardes > $RETENTION_DAYS jours"
echo "Termine"
Etape 12 : Tableaux (arrays)
Les tableaux stockent plusieurs valeurs. Utilisez () pour declarer, ${array[i]} pour acceder.
#!/bin/bash
# Declaration
serveurs=("srv-web-01" "srv-db-01" "srv-mail-01")
# Acces
echo "${serveurs[0]}" # Premier element
echo "${serveurs[@]}" # Tous
echo "${#serveurs[@]}" # Nombre d'elements
# Ajout
serveurs+=("srv-backup-01")
# Iteration
for srv in "${serveurs[@]}"; do
if ping -c 1 -W 2 "$srv" > /dev/null 2>&1; then
echo "$srv : UP"
else
echo "$srv : DOWN"
fi
done
# Tableau associatif (Bash 4+)
declare -A ports
ports[web]=80
ports[ssh]=22
ports[mysql]=3306
for service in "${!ports[@]}"; do
echo "$service ecoute sur ${ports[$service]}"
done
Etape 13 : Debug et logs
Pour deboguer, activez le mode trace avec set -x. Ajoutez des logs structures pour tracer l’execution en production.
#!/bin/bash
# Script avec debug
LOG_FILE="/var/log/monscript.log"
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') [$1] $2" | tee -a "$LOG_FILE"
}
# Activer le trace pour une section
set -x
commande_a_debugger
set +x
# Ou au lancement : bash -x monscript.sh
# Exemple complet
log "INFO" "Demarrage du script"
if ! curl -sf "https://itskillscenter.io" > /dev/null; then
log "ERROR" "Site inaccessible"
exit 1
fi
log "INFO" "Site OK"
# Verifier la syntaxe sans executer
# bash -n monscript.sh
Etape 14 : Planifier et integrer
Une fois votre script teste, planifiez-le avec cron pour une execution automatique. Stockez-le dans /usr/local/bin pour qu’il soit accessible partout.
# Placer le script sudo cp backup_mysql.sh /usr/local/bin/ sudo chmod +x /usr/local/bin/backup_mysql.sh sudo chown root:root /usr/local/bin/backup_mysql.sh # Editer le crontab crontab -e # Ajouter (tous les jours a 2h du matin) 0 2 * * * /usr/local/bin/backup_mysql.sh >> /var/log/backup.log 2>&1 # Verifier crontab -l # Tester manuellement /usr/local/bin/backup_mysql.sh # Controller les logs tail -f /var/log/backup.log
Erreurs
Oublier les guillemets autour des variables : sans guillemets, $var est decoupe par les espaces. Si fichier="mon fichier.txt", alors rm $fichier supprime « mon » et « fichier.txt » separement. Toujours utiliser rm "$fichier".
Espaces autour de = : x = 5 est une erreur en Bash. Il faut x=5 sans espaces. Bash interprete x = 5 comme la commande x avec arguments « = » et « 5 ».
Ne pas gerer les erreurs : un script qui continue apres une erreur peut causer des degats. Utilisez set -euo pipefail en haut du script, et verifiez les codes de retour des commandes critiques.
Utiliser sh au lieu de bash : lancer avec sh monscript.sh utilise un shell minimal qui ne comprend pas les tableaux, [[ ]], ou les fonctions avancees. Utilisez toujours bash monscript.sh ou rendez le script executable avec le bon shebang.
Hardcoder les chemins absolus : un script qui contient /home/admin/projet ne marchera pas chez un autre utilisateur. Utilisez $HOME, $(dirname $0), ou des variables de configuration.
Stocker les mots de passe en clair : ne mettez jamais de mots de passe directement dans les scripts. Utilisez des fichiers .env protection 600, ou mieux, des gestionnaires de secrets comme Vault.
Checklist
Shebang #!/bin/bash en premiere ligne.
Script rendu executable avec chmod +x.
set -euo pipefail ajoute pour la robustesse.
Variables entre guillemets doubles pour gerer les espaces.
Arguments valides avant utilisation (verification $#).
Codes de retour verifies avec $? ou if commande.
Fonctions utilisees pour factoriser le code repetitif.
Variables locales dans les fonctions avec local.
Logs horodates ecrits dans un fichier dedie.
Piege trap cleanup EXIT pour nettoyage automatique.
Aucun mot de passe en dur dans le script.
Test de syntaxe avec bash -n script.sh.
Test complet en environnement de dev avant prod.
Planification via cron ou systemd timer.
Rotation des logs configuree (logrotate).