Comment utiliser` while read ' (Bash) pour lire la dernière ligne d'un fichier s'il n'y a pas de nouvelle ligne à la fin du fichier?
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, cela sautera effectivement la dernière ligne.
j'ai cherché partout une solution et j'ai trouvé ce:
quand read atteint la fin du fichier à la place de fin de ligne, il lit dans le données et les affecter aux variables, mais il sort avec un état différent de zéro. Si votre boucle est construite "alors lire ;ne trucs ;fait
donc au lieu de tester la sortie read statut directement, tester un drapeau, et avoir la commande read a placé ce drapeau de dans le corps de la boucle. De cette façon indépendamment de lit statut de sortie, la le corps entier de la boucle fonctionne, parce que lire était juste une liste de commandes dans la boucle comme les autres, pas un facteur décisif de si la boucle obtenez de course à tous.
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 while
boucle que j'avais plus tôt, c'est-à-dire sans codage dur de l'emplacement du fichier d'entrée?
7 réponses
dans votre premier exemple, je suppose que vous lisez stdin. Pour faire la même chose avec le deuxième bloc de code, vous avez juste à supprimer la redirection et echo $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
il fonctionne avec à peu près tout sauf les caractères nuls dans l'entrée:
- fichiers qui commencent ou se terminent par des lignes vides
- les Lignes qui commencent ou se terminent par des espaces
- les Fichiers qui n'ont pas de terminaison de retour à la ligne
en utilisant grep
avec une boucle while:
while IFS= read -r line; do
echo "$line"
done < <(grep "" file)
en utilisant grep .
au lieu de grep ""
ignorer les lignes vides.
Remarque:
en utilisant
IFS=
garde n'importe quelle indentation de ligne intacte.le fichier sans nouvelle ligne à la fin n'est pas un fichier texte unix standard.
au Lieu de lire, essayez d'utiliser GNU Coreutilsté,chat, etc.
stdin
readvalue=$(tee)
echo $readvalue
dans le fichier
readvalue=$(cat filename)
echo $readvalue
La question fondamentale ici est que read
renvoie errorlevel 1 quand il rencontre EOF, même s'il alimente correctement la variable.
vous pouvez donc utiliser errorlevel de read
tout de suite dans votre boucle, otherwize, les dernières données ne seront pas analysées. Mais vous pourriez 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 souhaitez une très bonne façon d'analyser vos lignes, vous devez utiliser:
IFS='' read -r LINE
n'oubliez pas que:
- nul caractère sera ignoré
- si vous vous en tenez à l'aide de
echo
pour imiter le comportement decat
vous aurez besoin de forceecho -n
dès détection D'EOF (vous pouvez utiliser la condition[ "$eof" == true ]
)
la réponse de@Netcoder est bonne, cette optimisation élimine les lignes vierges intempestives, permet également à la dernière ligne de ne pas avoir de nouvelle ligne, si c'est ainsi que l'original était.
DONE=false
NL=
until $DONE ;do
if ! read ; then DONE=true ; NL='-n ';fi
echo $NL$REPLY
done
j'ai utilisé une variante de ceci pour créer 2 fonctions pour permettre la tuyauterie du texte qui inclut 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
}
(en passant - que $1 permet de tuyau sinon, il suffit de se traduit par des arguments)
C'est le modèle que j'ai utilisé:
while read -r; do
echo "${REPLY}"
done
[[ ${REPLY} ]] && echo "${REPLY}"
ce qui fonctionne parce que même les while
boucle se termine par le "test" de la read
retourne une valeur non-nulle code, read
occupe toujours la variable intégrée $REPLY
(ou toutes les variables que vous choisissez d'assigner avec