abstrait
Imprimer des lignes sans nouvelle ligne, ajoutez une nouvelle ligne uniquement s'il y a une autre ligne à imprimer.
$ printf 'one\ntwo\n' |
awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'; echo " done"
one
two done
Autres solutions
Si nous travaillions avec un fichier, nous pouvons simplement en tronquer un caractère (s'il se termine sur une nouvelle ligne):
removeTrailNewline () {[[$ (tail -c 1 "$ 1")]] || tronquer -s-1 "$ 1"; }
C'est une solution rapide car il ne doit lire qu'un seul caractère du fichier, puis le supprimer directement ( truncate
) sans lire le fichier entier.
Cependant, lorsque vous travaillez avec des données de stdin (un flux), les données doivent être lues en entier. Et, il est "consommé" dès qu'il est lu. Pas de retour en arrière (comme avec tronquer). Pour trouver la fin d'un flux, nous devons lire jusqu'à la fin du flux. À ce stade, il n'y a aucun moyen de revenir sur le flux d'entrée, les données ont déjà été "consommées". Cela signifie que les données doivent être stockées dans une forme de tampon jusqu'à ce que nous correspondions à la fin du flux, puis que nous fassions quelque chose avec les données dans le tampon.
La solution la plus évidente consiste à convertir le flux en fichier et à traiter ce fichier. Mais la question demande une sorte de filtre du flux. Pas sur l'utilisation de fichiers supplémentaires.
variable
La solution naïve serait de capturer la totalité de l'entrée dans une variable:
FilterOne(){ filecontents=$(cat; echo "x"); # capture the whole input
filecontents=${filecontents%x}; # Remove the "x" added above.
nl=$'\n'; # use a variable for newline.
printf '%s' "${filecontents%"$nl"}"; # Remove newline (if it exists).
}
printf 'one\ntwo' | FilterOne ; echo 1done
printf 'one\ntwo\n' | FilterOne ; echo 2done
printf 'one\ntwo\n\n' | FilterOne ; echo 3done
Mémoire
Il est possible de charger un fichier entier en mémoire avec sed. Dans sed, il est impossible d'éviter la nouvelle ligne de fin sur la dernière ligne. GNU sed peut éviter d'imprimer une nouvelle ligne de fin, mais uniquement si le fichier source le manque déjà. Donc, non, simple sed ne peut pas aider.
Sauf sur GNU awk avec l' -z
option:
sed -z 's/\(.*\)\n$/\1/'
Avec awk (n'importe quel awk), slurpez tout le flux, et printf
ce sans la nouvelle ligne de fin.
awk ' { content = content $0 RS }
END { gsub( "\n$", "", content ); printf( "%s", content ) }
'
Le chargement d'un fichier entier en mémoire n'est peut-être pas une bonne idée, il peut consommer beaucoup de mémoire.
Deux lignes en mémoire
Dans awk, nous pouvons traiter deux lignes par boucle en stockant la ligne précédente dans une variable et en imprimant la présente:
awk 'NR>1{print previous} {previous=$0} END {printf("%s",$0)}'
Traitement direct
Mais nous pourrions faire mieux.
Si nous imprimons la ligne actuelle sans nouvelle ligne et n'imprimons une nouvelle ligne que lorsqu'une ligne suivante existe, nous traitons une ligne à la fois et la dernière ligne n'aura pas de nouvelle ligne de fin:
awk 'NR == 1 {printf ("% s", $ 0); next}; {printf ("\ n% s", $ 0)} '
Ou, écrit d'une autre manière:
awk 'NR>1{ print "" }; { printf( "%s", $0 ) }'
Ou:
awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'
Donc:
$ printf 'one\ntwo\n' | awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'; echo " done"
one
two done
chomp
, carchomp
seulement supprime au plus une nouvelle ligne de fin.