← Réseaux & systèmes

Shell scripting Bash & redirections

Sommaire

Le shell n'est pas seulement une invite de commandes : c'est un langage complet, et l'outil d'automatisation de base d'un administrateur système.

1. Flux standards et redirections

Toute commande dispose de trois flux, identifiés par un numéro de canal.

FluxCanalDescriptionRedirection
STDIN0Entrée standard (clavier)<
STDOUT1Sortie standard (écran)> écraser, >> ajouter
STDERR2Sortie d'erreur2>, 2>>
ls > liste.txt              # écrase le fichier
ls >> liste.txt             # ajoute à la fin
ls /inexistant 2> erreurs.log     # seulement les erreurs
ls > sortie.log 2>&1        # les deux flux dans le même fichier
ls &> sortie.log            # écriture équivalente, plus courte
ls /inexistant 2> /dev/null # faire taire les erreurs

wc -l < fichier.txt         # lire depuis un fichier

/dev/null est un fichier spécial qui absorbe tout ce qu'on lui envoie : la « corbeille » du système. Utile pour supprimer un affichage sans supprimer la commande.

L'ordre compte : 2>&1 signifie « envoyer STDERR là où pointe actuellement STDOUT ». Écrit avant la redirection de STDOUT, il ne fonctionne pas. cmd > f.log 2>&1 est correct, cmd 2>&1 > f.log ne l'est pas.

2. Tubes et enchaînements

ps aux | grep apache | wc -l        # tube : la sortie devient l'entrée

commande1 ; commande2               # enchaîner quoi qu'il arrive
commande1 && commande2               # commande2 seulement si commande1 réussit
commande1 || commande2               # commande2 seulement si commande1 échoue

ls /tmp > /dev/null && echo "La commande a réussi"
mkdir /tmp/test || echo "Échec de la création"

Le tube (|) redirige STDOUT vers STDIN de la commande suivante. C'est ce qui permet de composer de petits outils plutôt que d'écrire un gros programme — le principe fondateur d'Unix.

&& et || s'appuient sur le code de retour : 0 signifie succès, toute autre valeur un échec.

3. grep, awk, sed et compagnie

grep "erreur" /var/log/syslog        # lignes contenant un motif
grep -i "erreur" fichier             # sans tenir compte de la casse
grep -v "info" fichier               # inverser : lignes NE contenant PAS
grep -r "mot" /etc/                  # récursif
grep -c "404" access.log             # compter les lignes correspondantes

cut -d: -f1 /etc/passwd              # 1er champ, séparateur ':'
sort fichier.txt | uniq -c           # trier puis compter les doublons
wc -l fichier.txt                    # compter les lignes

awk '{print $1, $7}' access.log      # afficher des colonnes
awk -F: '$3 >= 1000 {print $1}' /etc/passwd   # comptes utilisateurs

sed 's/ancien/nouveau/g' f.txt       # substituer (affichage)
sed -i 's/ancien/nouveau/g' f.txt    # substituer dans le fichier

Exemple d'enchaînement typique : les dix adresses IP les plus présentes dans un journal Apache.

awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10

4. Écrire un script

#!/bin/bash
# backup.sh — sauvegarde quotidienne
# Auteur : ...   Date : ...

echo "Backup exécuté à $(date)" >> ~/backup.log
chmod +x backup.sh
./backup.sh

5. Variables

NOM="BTS SIO"              # pas d'espace autour du '='
echo "Bonjour $NOM"        # guillemets doubles : la variable est remplacée
echo 'Bonjour $NOM'        # guillemets simples : affichage littéral
echo "${NOM}_2025"         # accolades pour délimiter le nom

FICHIER="/var/log/syslog"
echo "Taille : $(wc -l < "$FICHIER") lignes"

Variables réservées

VariableContenu
$0Nom du script
$1, $2Arguments passés au script
$#Nombre d'arguments
$@Tous les arguments
$?Code de retour de la dernière commande (0 = succès)
$$PID du script en cours

Variables d'environnement

env                        # lister les variables d'environnement
echo $PATH $HOME $USER

maVar="Bonjour"            # variable de shell : locale à la session
export maVar               # devient variable d'environnement, héritée par les enfants
export PATH=$PATH:.        # ajouter le répertoire courant au PATH

Différence à retenir : une variable simplement déclarée n'apparaît pas dans env et n'est pas visible depuis un script, car celui-ci s'exécute dans un shell enfant. export la fait passer aux processus fils. C'est exactement ce que teste l'exercice du TP.

Toujours entourer les variables de guillemets doubles : "$FICHIER". Sans eux, un nom contenant un espace est découpé en plusieurs arguments — source d'erreurs difficiles à diagnostiquer.

6. Conditions

if [ "$1" == "admin" ]; then
    echo "Accès autorisé"
elif [ -z "$1" ]; then
    echo "Aucun argument fourni"
else
    echo "Accès refusé"
fi
TestVrai si…
-f fichierLe fichier existe et est un fichier ordinaire
-d dossierLe répertoire existe
-e cheminL'élément existe, quel que soit son type
-r, -w, -xDroit de lecture / écriture / exécution
-z "$var"La chaîne est vide
-n "$var"La chaîne n'est pas vide
"$a" == "$b"Chaînes égales
$a -eq $bNombres égaux (-ne, -lt, -gt, -le, -ge)

Les espaces à l'intérieur des crochets sont obligatoires : [ "$a" == "b" ] et non ["$a"=="b"]. [ est une commande, pas une ponctuation.

case

case "$1" in
    start)   echo "Démarrage" ;;
    stop)    echo "Arrêt" ;;
    restart) echo "Redémarrage" ;;
    *)       echo "Usage : $0 {start|stop|restart}" ;;
esac

7. Boucles

# for sur une liste de fichiers
for item in *.txt; do
    echo "Fichier trouvé : $item"
done

# for numérique
for i in {1..5}; do
    echo "Itération $i"
done

# while, ligne par ligne dans un fichier
while read ligne; do
    echo "> $ligne"
done < fichier.txt

# until
compteur=0
until [ $compteur -ge 3 ]; do
    echo $compteur
    compteur=$((compteur + 1))
done

$(( )) réalise les calculs arithmétiques. break sort de la boucle, continue passe à l'itération suivante.

8. Fonctions et code de retour

#!/bin/bash

verifier_fichier() {
    if [ ! -f "$1" ]; then
        echo "Erreur : $1 introuvable" >&2
        return 1
    fi
    return 0
}

if [ $# -eq 0 ]; then
    echo "Usage : $0 <fichier>" >&2
    exit 1
fi

if verifier_fichier "$1"; then
    echo "toto" >> "$1"
    echo "Ligne ajoutée à $1"
else
    exit 1
fi