web-dev-qa-db-fra.com

Lecture de fichiers entrants en ligne avec commande de script Shell ignore la dernière ligne.

J'utilise généralement la commande read pour lire ligne par ligne un fichier d'entrée dans le script Shell. Un exemple de code, tel que celui ci-dessous, donne un résultat incorrect si aucune nouvelle ligne n'est insérée à la fin de la dernière ligne du fichier d'entrée, blah.txt.

#!/bin/sh

while read line
do
echo $line
done <blah.txt

Donc, si le fichier d’entrée lit quelque chose comme:

One 
Two
Three
Four

et je ne frappe pas retour après quatre, le script ne parvient pas à lire la dernière ligne, et imprime 

One
Two
Three

Maintenant, si je laisse une ligne vierge supplémentaire après quatre, comme,

One 
Two
Three
Four
//blank line

la sortie imprime toutes les lignes, y compris Quatre. Cependant, ce n'est pas le cas lorsque je lis une ligne à l'aide de la commande cat; toutes les lignes, y compris la dernière, sont imprimées sans que je doive ajouter une ligne vierge supplémentaire à la fin.

Quelqu'un a des idées sur pourquoi cela se produit? Les scripts que je crée seront principalement exécutés par d'autres. Il n'est donc pas nécessaire qu'ils ajoutent une ligne vierge supplémentaire à la fin de chaque fichier d'entrée.

J'essaie de comprendre cela depuis des siècles. Je vous en serais reconnaissant si vous aviez des solutions (bien sûr, la commande cat en est une, mais j'aimerais connaître la raison de cette lecture qui ne fonctionne pas aussi bien).

18
Caife

read lit jusqu'à ce qu'il trouve un caractère de nouvelle ligne ou la fin du fichier et renvoie un code de sortie différent de zéro s'il rencontre une fin de fichier. Il est donc tout à fait possible qu'il lise une ligne et renvoie un code de sortie non nul.

Par conséquent, le code suivant n'est pas sûr si l'entrée peut ne pas être terminée par une nouvelle ligne:

while read LINE; do
  # do something with LINE
done

parce que le corps de la while ne sera pas exécuté sur la dernière ligne.

Techniquement, un fichier ne se terminant pas par une nouvelle ligne n'est pas un fichier texte et les outils texte peuvent échouer de manière étrange sur un tel fichier. Cependant, je suis toujours réticent à me rabattre sur cette explication.

Une façon de résoudre le problème est de vérifier si ce qui a été lu est non vide (-n):

while read -r LINE || [[ -n $LINE ]]; do
  # do something with LINE
done

Parmi les autres solutions, utilisez mapfile pour lire le fichier dans un tableau, le transférer à travers un utilitaire dont la garantie est de terminer correctement la dernière ligne (grep ., par exemple, si vous ne voulez pas utiliser de lignes vides), traitement itératif avec un outil comme awk (ce qui est généralement ma préférence).

Notez que -r est presque certainement nécessaire dans la variable read; read ne réinterprète pas \- dans l'entrée.

19
rici
8
Steven Penny

Réponse en ligne:

IFS=$'\n'; for line in $(cat file.txt); do echo "$line" ; done
4
sandino

Utilisez la boucle while comme ceci:

while IFS= read -r line || [ -n "$line" ]; do
  echo "$line"
done <file

Ou en utilisant grep avec la boucle while:

while IFS= read -r line; do
  echo "$line"
done < <(grep "" file)

Utiliser grep . au lieu de grep "" sautera les lignes vides.

Remarque: 

  1. L'utilisation de IFS= conserve l'indentation de ligne intacte.

  2. Vous devriez presque toujours utiliser l'option -r avec read.

  3. Ne lisez pas les lignes avec for

  4. Un fichier sans nouvelle ligne à la fin n'est pas un fichier texte unix standard.

2
Jahid

Le code ci-dessous avec la boucle "En lecture" redirigée me convient

while read LINE
do
      let count++
      echo "$count $LINE"

done < $FILENAME

echo -e "\nTotal $count Lines read"
0
Aniket Thakur