Disons que j’ai le script Bash suivant:
while read SCRIPT_SOURCE_LINE; do
echo "$SCRIPT_SOURCE_LINE"
done
J'ai remarqué que pour les fichiers sans nouvelle ligne à la fin, la dernière ligne sera ignorée.
J'ai cherché une solution et j'ai trouvé ceci :
Lorsque la lecture atteint la fin du fichier à la place de fin de ligne, il est indiqué dans le données et les attribuer aux variables, mais il existe avec un statut différent de zéro . Si votre boucle est construite "alors que Lisez; faites des choses; fini
Donc, au lieu de tester la sortie de lecture statut directement, tester un drapeau et avoir la commande de lecture a défini cet indicateur à partir de dans le corps de la boucle. De cette façon quel que soit l'état de sortie de la lecture, le tout le corps de la boucle s'exécute, car read était juste une de la liste de commandes dans la boucle comme un autre, pas un facteur décisif de si la boucle sera être couru du tout.
DONE=false until $DONE ;do read || DONE=true # process $REPLY here done < /path/to/file.in
Comment puis-je réécrire cette solution pour qu’elle se comporte exactement comme la boucle while
que j’avais précédemment, c’est-à-dire sans codage en dur de l’emplacement du fichier d’entrée?
Dans votre premier exemple, je suppose que vous lisez stdin. Pour faire la même chose avec le deuxième bloc de code, il vous suffit de supprimer la redirection et d’écho $ REPLY:
DONE=false
until $DONE ;do
read || DONE=true
echo $REPLY
done
J'utilise la construction suivante:
while IFS= read -r LINE || [[ -n "$LINE" ]]; do
echo "$LINE"
done
Cela fonctionne avec pratiquement tout sauf les caractères nuls dans l'entrée:
Utilisation de 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:
L'utilisation de IFS=
conserve l'indentation de ligne intacte.
Vous devriez presque toujours utiliser l'option -r avec read.
Un fichier sans nouvelle ligne à la fin n'est pas un fichier texte unix standard.
Au lieu de read , essayez d’utiliser GNU Coreutils like tee , cat , etc.
de stdin
readvalue=$(tee)
echo $readvalue
de fichier
readvalue=$(cat filename)
echo $readvalue
Le problème fondamental ici est que read
renvoie le niveau d'erreur 1 lorsqu'il rencontre EOF, même s'il alimente toujours correctement la variable.
Ainsi, vous pouvez utiliser le niveau d'erreur read
tout de suite dans votre boucle. Sinon, les dernières données ne seront pas analysées. Mais vous pouvez faire ceci:
eof=
while [ -z "$eof" ]; do
read SCRIPT_SOURCE_LINE || eof=true ## detect eof, but have a last round
echo "$SCRIPT_SOURCE_LINE"
done
Si vous voulez un moyen très solide d’analyser vos lignes, vous devez utiliser:
IFS='' read -r LINE
Rappelez-vous que:
echo
pour imiter le comportement de cat
, vous devrez forcer un echo -n
à EOF détecté (vous pouvez utiliser la condition [ "$eof" == true ]
)La réponse de @ Netcoder est bonne, cette optimisation élimine les fausses lignes vierges et permet également à la dernière ligne de ne pas avoir de saut de ligne, si tel était l'original.
DONE=false
NL=
until $DONE ;do
if ! read ; then DONE=true ; NL='-n ';fi
echo $NL$REPLY
done
J'ai utilisé une variante de cela pour créer 2 fonctions permettant de rediriger du texte avec un '[' pour garder grep heureux. (vous pouvez ajouter d'autres traductions)
function grepfix(){
local x="$@";
if [[ "$x" == '-' ]]; then
local DONE=false
local xx=
until $DONE ;do
if ! IFS= read ; then DONE=true ; xx="-n "; fi
echo ${xx}${REPLY//\[/\\\[}
done
else
echo "${x//\[/\\\[}"
fi
}
function grepunfix(){
local x="$@";
if [[ "$x" == '-' ]]; then
local DONE=false
local xx=
until $DONE ;do
if ! IFS= read ; then DONE=true ; xx="-n "; fi
echo ${xx}${REPLY//\\\[/\[}
done
else
echo "${x//\\\[/\[}"
fi
}
(passant - comme $ 1 active le pipe sinon traduit uniquement les arguments)
C'est le motif que j'ai utilisé:
while read -r; do
echo "${REPLY}"
done
[[ ${REPLY} ]] && echo "${REPLY}"
Ce qui fonctionne car même si la boucle while
se termine avec le "test" de la read
avec un code différent de zéro, read
remplit toujours la variable incorporée $REPLY
(ou les variables que vous choisissez d'attribuer avec read
).