Débutant

Bash débutant : maîtriser read et mapfile sans perte

Apprenez à lire un fichier ligne par ligne en Bash avec read et mapfile, sans casser les espaces, ni perdre les dernières lignes.

Bash débutant : maîtriser read et mapfile sans perte
Difficulté
Intermédiaire

Lire un fichier ligne par ligne en Bash semble simple, mais les premiers essais perdent souvent des espaces, mangent des antislashs ou oublient la dernière ligne. Ce tutoriel vous montre une méthode sûre avec read, puis une alternative pratique avec mapfile.

Vous allez manipuler de vrais exemples, vérifier le résultat à chaque étape et comprendre où se glissent les erreurs classiques. L’objectif est de traiter un fichier texte sans altérer son contenu ligne par ligne.

Prérequis

  • Avoir accès à un shell Bash.
  • Savoir exécuter un script avec bash script.sh ou après lui avoir donné les droits d’exécution.
  • Disposer d’un fichier texte de test, ou accepter d’en créer un pendant le tutoriel.

Matériel nécessaire

  • Un terminal avec Bash
  • Un éditeur de texte pour créer un script
  • Un fichier texte de test

Étapes

  1. Créer un fichier de test représentatif

    Vue de dessus d’un ordinateur portable sur un bureau nu pendant la création d’un fichier de test, avec une zone d’édition affichant plusieurs lignes de longueurs variées et des espacements visibles, sans texte lisible.

    Commencez par créer un fichier contenant des cas qui posent souvent problème : espaces en début de ligne, espaces internes, antislashs et dernière ligne sans saut de ligne final. Cela permet de vérifier tout de suite si votre lecture conserve bien les données.

    cat > donnees.txt <<'EOF'
      ligne avec espaces au début
    mot   avec   plusieurs   espaces
    chemin\avec\antislashs
    texte normal
    EOF
    printf '%s' 'derniere ligne sans saut final' >> donnees.txt

    Le fichier obtenu contient plusieurs lignes ordinaires, puis une dernière ligne ajoutée sans retour à la ligne final. C’est précisément ce cas qui révèle beaucoup de boucles Bash fragiles.

  2. Écrire une première boucle sûre avec read

    Créez maintenant un script minimal qui lit le fichier ligne par ligne sans transformation involontaire. La forme de base à retenir est la suivante.

    #!/usr/bin/env bash
    
    while IFS= read -r ligne; do
      printf '[%s]\n' "$ligne"
    done < donnees.txt

    Exécutez le script et observez la sortie. Les crochets vous aident à voir les espaces de début et de fin qui passeraient sinon inaperçus.

  3. Comparer avec une boucle fragile

    Testez ensuite une version volontairement mauvaise pour constater la différence. Cette comparaison ancre plus vite les bons réflexes.

    #!/usr/bin/env bash
    
    while read ligne; do
      printf '[%s]\n' "$ligne"
    done < donnees.txt

    Selon le contenu du fichier, cette boucle peut modifier des espaces significatifs et interpréter certains antislashs. Vous obtenez alors une lecture qui n’est plus fidèle au texte d’origine.

  4. Traiter aussi la dernière ligne sans saut final

    Ordinateur portable vu de côté pendant une lecture ligne par ligne, avec l’accent sur une dernière ligne traitée malgré une fin abrupte sans saut final.

    Améliorez la boucle pour gérer le cas où le fichier se termine sans retour à la ligne. C’est un détail fréquent dans des fichiers générés automatiquement.

    #!/usr/bin/env bash
    
    while IFS= read -r ligne || [ -n "$ligne" ]; do
      printf '[%s]\n' "$ligne"
    done < donnees.txt

    Relancez le script sur le fichier de test. La dernière ligne, même sans saut final, est maintenant traitée elle aussi.

  5. Afficher les numéros de ligne pendant la lecture

    Ajoutez un compteur pour repérer plus facilement une ligne problématique. C’est utile pour déboguer un traitement, vérifier une transformation ou tracer une erreur.

    #!/usr/bin/env bash
    
    numero=0
    while IFS= read -r ligne || [ -n "$ligne" ]; do
      numero=$((numero + 1))
      printf '%d : [%s]\n' "$numero" "$ligne"
    done < donnees.txt

    La logique de lecture reste la même. Vous enrichissez seulement la sortie avec un numéro incrémenté à chaque tour.

  6. Stocker chaque ligne dans un tableau à la main

    Si vous devez réutiliser les lignes plus tard, remplissez un tableau pendant la lecture. Cela reste pratique quand vous voulez à la fois traiter au fil de l’eau et conserver les données.

    #!/usr/bin/env bash
    
    declare -a lignes=()
    while IFS= read -r ligne || [ -n "$ligne" ]; do
      lignes+=("$ligne")
    done < donnees.txt
    
    printf 'Total : %d\n' "${#lignes[@]}"
    printf 'Premiere : [%s]\n' "${lignes[0]}"

    Chaque élément du tableau correspond à une ligne lue. Les guillemets autour de "$ligne" évitent que son contenu soit redécoupé au moment de l’ajout.

  7. Charger directement le fichier avec mapfile

    Gros plan sur un ordinateur portable montrant un chargement direct d’un fichier dans une structure de tableau en mémoire, avec les mains en train de taper.

    Passez maintenant à mapfile, qui remplit un tableau en une commande. Pour beaucoup de scripts simples, c’est la solution la plus concise.

    #!/usr/bin/env bash
    
    mapfile -t lignes < donnees.txt
    
    printf 'Total : %d\n' "${#lignes[@]}"
    printf 'Ligne 1 : [%s]\n' "${lignes[0]}"
    printf 'Ligne 2 : [%s]\n' "${lignes[1]}"

    Exécutez le script et vérifiez le nombre d’éléments puis leur contenu. Vous disposez maintenant d’un tableau prêt à parcourir.

  8. Parcourir le tableau rempli par mapfile

    Une fois le tableau chargé, parcourez-le explicitement. Vous obtenez une lecture claire et prévisible de chaque ligne.

    #!/usr/bin/env bash
    
    mapfile -t lignes < donnees.txt
    
    for ligne in "${lignes[@]}"; do
      printf '[%s]\n' "$ligne"
    done

    Le point important ici est l’expansion "${lignes[@]}" entre guillemets. Elle transmet chaque élément du tableau comme une ligne distincte, même si cet élément contient des espaces.

  9. Choisir entre read et mapfile selon le besoin

    Utilisez read si vous voulez traiter le fichier progressivement, ligne par ligne, pendant la lecture. Cette approche convient bien quand chaque ligne déclenche une action immédiate.

    Utilisez mapfile si vous voulez d’abord charger toutes les lignes, puis les manipuler comme un tableau. Cette approche rend certains scripts plus courts et plus lisibles.

  10. Éviter les constructions qui cassent les lignes

    Écartez les schémas qui redécoupent le contenu au lieu de respecter les lignes d’origine. Un exemple classique consiste à boucler sur une substitution de commande.

    for x in $(cat donnees.txt); do
      printf '[%s]\n' "$x"
    done

    Cette construction ne lit pas le fichier ligne par ligne de façon fiable. Elle travaille sur des mots découpés par le shell, ce qui détruit les espaces significatifs et la structure du fichier.

  11. Tester explicitement les espaces et les antislashs

    Validez votre script avec des sorties qui rendent les caractères sensibles visibles. Le plus simple est d’encadrer la valeur affichée et de répéter les essais sur votre fichier de test.

    while IFS= read -r ligne || [ -n "$ligne" ]; do
      printf '>[%s]<\n' "$ligne"
    done < donnees.txt

    Si une ligne commence par des espaces, finit par des espaces ou contient des antislashs, cette présentation vous aide à vérifier qu’aucune transformation involontaire n’a eu lieu.

  12. Assembler un modèle réutilisable pour vos scripts

    Terminez en conservant deux modèles prêts à coller dans vos scripts. Le premier sert au traitement progressif, le second au chargement dans un tableau.

    #!/usr/bin/env bash
    
    # Mode 1 : lecture progressive
    while IFS= read -r ligne || [ -n "$ligne" ]; do
      printf '[%s]\n' "$ligne"
    done < fichier.txt
    
    # Mode 2 : chargement complet
    mapfile -t lignes < fichier.txt
    for ligne in "${lignes[@]}"; do
      printf '[%s]\n' "$ligne"
    done

    Avec ces deux patrons, vous couvrez l’essentiel des besoins débutants et intermédiaires. Vous savez surtout éviter les pièges qui altèrent les espaces, les antislashs ou la dernière ligne.

Erreurs fréquentes & dépannage

  • Oublier IFS= : les espaces de début ou de fin peuvent ne plus être lus comme prévu.
  • Oublier -r : les antislashs peuvent être interprétés au lieu d’être conservés tels quels.
  • Ignorer la dernière ligne sans saut final : la boucle simple avec read peut ne pas la traiter.
  • Parcourir $(cat fichier) : vous obtenez des mots, pas des lignes fidèles au fichier.
  • Oublier les guillemets autour des expansions : une ligne contenant des espaces peut être redécoupée par le shell.

Astuces & pour aller plus loin

Commencez toujours vos tests avec un fichier qui contient des espaces de début, plusieurs espaces internes, des antislashs et une dernière ligne sans saut final. Si votre script gère bien ces cas, il sera bien plus robuste sur des fichiers ordinaires.

Pour déboguer, affichez chaque ligne entre délimiteurs visuels comme >[ et ]<. Cette habitude simple permet de repérer très vite une perte d’espaces ou une ligne oubliée.

Questions fréquentes

Pourquoi utiliser IFS= read -r au lieu de read tout court ?

Parce que cette forme protège mieux le contenu lu. IFS= évite de rogner des espaces significatifs, et -r conserve les antislashs tels quels.

Pourquoi la dernière ligne d’un fichier est-elle parfois ignorée ?

Si le fichier ne se termine pas par un saut de ligne, read peut signaler la fin de fichier même après avoir récupéré du texte. Le test || [ -n "$ligne" ] permet de traiter cette dernière donnée.

Quand choisir mapfile ?

Choisissez mapfile -t quand vous voulez charger toutes les lignes dans un tableau pour les parcourir ou les réutiliser ensuite. Si vous préférez traiter chaque ligne immédiatement, la boucle avec read reste très adaptée.

Est-ce que mapfile -t remplace toujours une boucle avec read ?

Non. mapfile -t est très pratique pour remplir un tableau, mais la boucle avec read reste utile quand vous voulez agir pendant la lecture ligne par ligne.

Pourquoi faut-il citer "${lignes[@]}" dans une boucle for ?

Les guillemets préservent chaque élément du tableau comme une unité, même s’il contient des espaces. Sans eux, le shell peut redécouper le contenu.

Conclusion

Vous disposez maintenant de deux méthodes fiables pour lire un fichier en Bash sans perdre d’informations. La boucle while IFS= read -r ligne || [ -n "$ligne" ] couvre la lecture progressive, tandis que mapfile -t simplifie le chargement en tableau.

Retenez surtout les pièges à éviter : lecture par défaut trop permissive, antislashs mal gérés, dernière ligne oubliée et expansions non citées. Avec ces patrons, vos scripts traiteront les fichiers texte de façon nettement plus sûre.

Commentaires · Aucun commentaire pour l'instant

Soyez le premier à réagir.

Laisser un commentaire