173 votes

Comment puis-je supprimer un saut de ligne s'il s'agit du dernier caractère d'un fichier ?

J'ai quelques fichiers pour lesquels j'aimerais supprimer le dernier saut de ligne s'il s'agit du dernier caractère d'un fichier. od -c me montre que la commande que j'exécute écrit bien le fichier avec une nouvelle ligne de queue :

0013600   n   t  >  \n

J'ai essayé quelques trucs avec sed mais le meilleur que j'ai pu trouver ne fait pas l'affaire :

sed -e '$s/\(.*\)\n$/\1/' abc

Avez-vous une idée de la façon de procéder ?

4 votes

Newline n'est qu'un caractère pour les newlines unix. Les sauts de ligne DOS ont deux caractères. Bien sûr, les caractères littéraux " \n "est composé de deux caractères. Lequel cherchez-vous réellement ?

3 votes

Bien que la représentation puisse être \n sous linux, c'est un caractère

10 votes

Pouvez-vous expliquer pourquoi vous voulez faire cela ? Les fichiers texte sont supposée pour se terminer par une fin de ligne, à moins qu'ils ne soient entièrement vides. Il me semble étrange que vous souhaitiez avoir un fichier aussi tronqué ?

234voto

pavium Points 7845
perl -pe 'chomp if eof' filename >filename2

ou, pour modifier le fichier sur place :

perl -pi -e 'chomp if eof' filename

[Note de l'éditeur : -pi -e était à l'origine -pie mais, comme l'ont noté plusieurs commentateurs et expliqué @hvd, cette dernière ne fonctionne pas].

Cela a été décrit comme un "blasphème perl" sur le site web awk que j'ai vu.

Mais, dans un test, ça a marché.

11 votes

Vous pouvez le rendre plus sûr en utilisant chomp . Et c'est mieux que d'avaler le dossier.

6 votes

Bien que ce soit un blasphème, cela fonctionne très bien. perl -i -pe 'chomp if eof' nom de fichier. Merci.

14 votes

Ce qui est drôle avec le blasphème et l'hérésie, c'est qu'ils sont généralement détestés parce qu'ils sont corrects. :)

64voto

mklement0 Points 12597

Vous pouvez profiter du fait que coquille substitutions de commandes supprimer les caractères de fin de ligne :

Formulaire simple qui fonctionne en bash, ksh, zsh :

printf %s "$(< in.txt)" > out.txt

Alternative portable (conforme à POSIX) (légèrement moins efficace) :

printf %s "$(cat in.txt)" > out.txt

Note :

  • Si in.txt se termine par multiple les caractères de nouvelle ligne, la substitution de commande supprime tous d'eux . Merci, Sparhawk (Il ne supprime pas les caractères d'espacement autres que les nouvelles lignes de queue).
  • Puisque cette approche lit l'intégralité du fichier d'entrée en mémoire il n'est conseillé que pour les petits fichiers.
  • printf %s assure qu'aucune nouvelle ligne n'est ajoutée à la sortie (c'est l'alternative conforme à POSIX à l'option non standard echo -n ; voir http://pubs.opengroup.org/onlinepubs/009696799/utilities/echo.html et https://unix.stackexchange.com/a/65819 )

A guide des autres réponses :

  • Si Perl est disponible, optez pour le réponse acceptée - c'est simple et efficace en mémoire (ne lit pas tout le fichier d'entrée en une seule fois).

  • Sinon, envisagez ghostdog74's Awk réponse - c'est obscure, mais aussi efficace en termes de mémoire ; a équivalent plus lisible (conforme à POSIX) est :

  • awk 'NR > 1 { print prev } { prev=$0 } END { ORS=""; print }' in.txt

  • L'impression est retardée d'une ligne afin que la dernière ligne puisse être gérée dans le processus d'impression. END où elle est imprimée sans la fin du bloc \n en raison du réglage du séparateur d'enregistrement de sortie ( OFS ) à une chaîne vide.

  • Si vous voulez un une solution verbeuse, mais rapide et robuste qui des modifications réelles en cours de route (par opposition à la création d'un fichier temporaire qui remplace ensuite l'original), pensez à de jrockway Perl script .

52voto

Thor Points 13562

Vous pouvez le faire avec head de GNU coreutils, il supporte les arguments qui sont relatifs à la fin du fichier. Donc pour laisser le dernier octet, utilisez :

head -c -1

Pour tester la présence d'un saut de ligne à la fin, vous pouvez utiliser tail et wc . L'exemple suivant enregistre le résultat dans un fichier temporaire et écrase ensuite l'original :

if [[ $(tail -c1 file | wc -l) == 1 ]]; then
  head -c -1 file > file.tmp
  mv file.tmp file
fi

Vous pouvez également utiliser sponge de moreutils pour effectuer des modifications "sur place" :

[[ $(tail -c1 file | wc -l) == 1 ]] && head -c -1 file | sponge file

Vous pouvez également créer une fonction générale réutilisable en l'insérant dans votre fichier .bashrc fichier :

# Example:  remove-last-newline < multiline.txt
function remove-last-newline(){
    local file=$(mktemp)
    cat > $file
    if [[ $(tail -c1 $file | wc -l) == 1 ]]; then
        head -c -1 $file > $file.tmp
        mv $file.tmp $file
    fi
    cat $file
}

Mise à jour

Comme l'a noté KarlWilbur dans les commentaires et utilisés dans Sorentar's réponse , truncate --size=-1 peut remplacer head -c-1 et prend en charge l'édition sur place.

4 votes

La meilleure solution jusqu'à présent. Elle utilise un outil standard que toutes les distributions Linux possèdent, et est concise et claire, sans aucune magie de sed ou perl.

2 votes

Bonne solution. Une seule modification : je pense que j'utiliserais truncate --size=-1 au lieu de head -c -1 puisqu'il ne fait que redimensionner le fichier d'entrée plutôt que de lire le fichier d'entrée, de l'écrire dans un autre fichier, puis de remplacer l'original par le fichier de sortie.

3 votes

Notez que head -c -1 supprimera le dernier caractère, qu'il s'agisse d'une nouvelle ligne ou non, c'est pourquoi vous devez vérifier si le dernier caractère est une nouvelle ligne avant de le supprimer.

17voto

Dennis Williamson Points 105818
head -n -1 abc > newfile
tail -n 1 abc | tr -d '\n' >> newfile

Edit 2 :

Voici un awk version (corrigé) qui n'accumule pas un tableau potentiellement énorme :

awk '{if (ligne) print line ; line=$0} END {printf $0}' abc

0 votes

Une bonne façon originale d'y penser. Merci Dennis.

0 votes

Vous avez raison. Je m'en remets à votre awk version. Il faut deux offsets (et un test différent) et je n'en ai utilisé qu'un seul. Cependant, vous pourriez utiliser printf au lieu de ORS .

0 votes

Vous pouvez faire de la sortie un pipe avec une substitution de processus : head -n -1 abc | cat <(tail -n 1 abc | tr -d '\n') | ...

11voto

ghostdog74 Points 86060

Gawk

   awk '{q=p;p=$0}NR>1{print q}END{ORS = ""; print p}' file

0 votes

Il me semble qu'il y a encore beaucoup de caractères... je l'apprends lentement :). Mais ça fait l'affaire. Merci ghostdog.

1 votes

awk '{ prev_line = line; line = $0; } NR > 1 { print prev_line; } END { ORS = ""; print line; }' file cela devrait être plus facile à lire.

0 votes

Pourquoi pas : awk 'NR>1 {print p} {p=$0} END {printf $0}' file .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X