En utilisant les systèmes de contrôle de version, je suis agacé par le bruit lorsque le diff dit No newline at end of file
.
Alors je me demandais: comment ajouter une nouvelle ligne à la fin d'un fichier pour se débarrasser de ces messages?
bash
shell
text-processing
newlines
k0pernikus
la source
la source
Réponses:
Pour désinfecter de manière récursive un projet, j'utilise cet oneliner:
Explication:
git ls-files -z
répertorie les fichiers dans le référentiel. Il faut un motif facultatif comme paramètre supplémentaire, ce qui peut être utile dans certains cas si vous souhaitez limiter l'opération à certains fichiers / répertoires. Au lieu de cela, vous pouvez utiliserfind -print0 ...
des programmes similaires pour répertorier les fichiers affectés - assurez-vous simplement qu’il émet desNUL
entrées délimitées.while IFS= read -rd '' f; do ... done
effectue une itération dans les entrées, en gérant en toute sécurité les noms de fichiers qui incluent des espaces et / ou des nouvelles lignes.tail -c1 < "$f"
lit le dernier caractère d'un fichier.read -r _
quitte avec un statut de sortie différent de zéro s'il manque une nouvelle ligne.|| echo >> "$f"
ajoute une nouvelle ligne au fichier si l'état de sortie de la commande précédente était différent de zéro.la source
find -name \*.java | while read f; do tail -n1 $f | read -r _ || echo >> $f; done
git ls-files
qui vous préservera néanmoins de la modification de fichiers qui ne sont pas suivis dans le contrôle de version.IFS=
Ajouter le séparateur est utile pour préserver les espaces blancs environnants. Les entrées terminées par un caractère nul ne sont pertinentes que si vous avez des fichiers ou des répertoires avec une nouvelle ligne dans leur nom, ce qui semble assez poussé, mais c'est la manière la plus correcte de gérer le cas générique, je suis d'accord. Juste une petite mise en garde: l'-d
option toread
n'est pas disponible dans POSIX sh.tail -n1 < "$f"
pour éviter les problèmes avec les noms de fichiers qui commencent par-
(tail -n1 -- "$f"
ne fonctionne pas pour le fichier appelé-
). Vous voudrez peut-être préciser que la réponse est maintenant spécifique à zsh / bash.Ici vous allez :
Et alternativement pour OS X
sed
:Cela ajoute
\n
à la fin du fichier uniquement s'il ne se termine pas déjà par une nouvelle ligne. Donc, si vous l'exécutez deux fois, il n'y aura pas d'autre nouvelle ligne:la source
man sed
:$ Match the last line.
mais peut-être que cela ne fonctionne que par accident. Votre solution fonctionne également.$
correspond à la dernière ligne, pourquoi n'ajoute-t-il pas une nouvelle ligne à une chaîne qui contient déjà une nouvelle ligne?$
. Dans une expression rationnelle, comme avec la forme/<regex>/
, il a la signification habituelle de "correspondance de fin de ligne". Autrement, utilisé comme adresse, sed lui donne le sens spécial "dernière ligne du fichier". Le code fonctionne parce que sed ajoute par défaut une nouvelle ligne à sa sortie si elle n'y est pas déjà. Le code "$ a \" dit simplement "correspond à la dernière ligne du fichier et n'y ajoute rien". Mais implicitement, sed ajoute la nouvelle ligne à chaque ligne traitée (telle que cette$
ligne), si ce n’est pas déjà fait./regex/
lui donne un sens différent. Les pages de manuel FreeBSD sont un peu plus informatives, je pense: freebsd.org/cgi/man.cgi?query=sedRegarde:
alors
echo "" >> noeol-file
devrait faire l'affaire. (Ou avez-vous eu l'intention de demander d'identifier ces fichiers et de les corriger?)edit a supprimé le
""
deecho "" >> foo
(voir le commentaire de @ yuyichao) edit2 l'a ajouté à""
nouveau ( mais voir le commentaire de @Keith Thompson)la source
""
n'est pas nécessaire (du moins pour bash) ettail -1 | wc -l
peut être utilisé pour trouver le fichier sans nouvelle ligne à la fin""
n'est pas nécessaire pour bash, mais j'ai déjà vu desecho
implémentations qui n'impriment rien lorsqu'elles sont appelées sans arguments (bien qu'aucune de celles que je puisse trouver maintenant ne le fasse).echo "" >> noeol-file
est probablement un peu plus robuste.printf "\n" >> noeol-file
est encore plus.csh
« secho
est celui connu à rien de sortie lorsqu'ils ne sont pas passé aucun argument. Mais alors, si nous voulons soutenir des coquilles non-Bourne, nous devrions le faireecho ''
au lieu de ceecho ""
queecho ""
nous ferions""<newline>
avecrc
oues
par exemple.tcsh
, contrairement à celacsh
, affiche une nouvelle ligne quand elle est invoquée sans arguments - quel que soit le réglage de$echo_style
.Une autre solution utilisant
ed
. Cette solution n'affecte que la dernière ligne et uniquement s'il\n
manque:Cela fonctionne essentiellement en ouvrant le fichier pour l’éditer à travers un script, le script est la seule
w
commande qui réécrit le fichier sur le disque. Il est basé sur cette phrase trouvée dans laed(1)
page de manuel:la source
Un moyen simple, portable et compatible POSIX d’ajouter une nouvelle ligne finale absente à un fichier texte serait:
Cette approche n’a pas besoin de lire l’ensemble du fichier; il peut simplement chercher à EOF et travailler à partir de là.
Cette approche n'a pas non plus besoin de créer des fichiers temporaires derrière votre dos (par exemple, sed -i), les liens physiques ne sont donc pas affectés.
echo ajoute une nouvelle ligne au fichier uniquement lorsque le résultat de la substitution de commande est une chaîne non vide. Notez que cela ne peut se produire que si le fichier n'est pas vide et que le dernier octet n'est pas une nouvelle ligne.
Si le dernier octet du fichier est une nouvelle ligne, tail le renvoie, puis la substitution de commande le supprime; le résultat est une chaîne vide. Le test -n échoue et l'écho ne s'exécute pas.
Si le fichier est vide, le résultat de la substitution de commande est également une chaîne vide et l'écho ne s'exécute pas à nouveau. Cela est souhaitable, car un fichier vide n'est pas un fichier texte non valide, ni équivalent à un fichier texte non vide avec une ligne vide.
la source
yash
si le dernier caractère du fichier est un caractère multi-octets (dans les environnements locaux UTF-8, par exemple) ou si l'environnement local est C et que le dernier octet du fichier est défini sur le huitième bit. Avec les autres shells (sauf zsh), cela n’ajouterait pas de nouvelle ligne si le fichier se terminait par un octet NUL (mais cela signifierait que l’entrée ne serait pas du texte même après l’ajout d’une nouvelle ligne).Ajouter newline indépendamment de:
Voici un moyen de vérifier si une nouvelle ligne existe à la fin avant d'en ajouter une, en utilisant Python:
la source
echo ""
semble plus robuste queecho -n '\n'
. Ou vous pouvez utiliserprintf '\n'
La solution la plus rapide est:
Est vraiment rapide.
Sur un fichier de taille moyenne,
seq 99999999 >file
cela prend des millisecondes.D'autres solutions prennent beaucoup de temps:
Fonctionne en ash, bash, lksh, mksh, ksh93, attsh et zsh mais pas yash.
Toutes les autres solutions présentées ici modifient l'horodatage du fichier.
Si vous avez besoin d'une solution portable pour yash (et tous les autres shells listés ci-dessus), la complexité peut devenir un peu plus complexe:
la source
Le moyen le plus rapide de vérifier si le dernier octet d'un fichier est une nouvelle ligne est de lire uniquement ce dernier octet. Cela pourrait être fait avec
tail -c1 file
. Cependant, la manière simpliste de vérifier si la valeur d'octet est une nouvelle ligne, en fonction du shell habituel, la suppression habituelle d'une nouvelle ligne dans une extension de commande échoue (par exemple) dans yash, lorsque le dernier caractère du fichier est un caractère UTF. 8 valeur.Le moyen correct, correct, compatible avec POSIX et tout le monde (raisonnable) de rechercher si le dernier octet d'un fichier est une nouvelle ligne consiste à utiliser xxd ou hexdump:
Ensuite, en comparant les résultats ci-dessus, vous
0A
obtiendrez un test robuste.Il est utile d'éviter d'ajouter une nouvelle ligne à un fichier sinon vide.
Fichier qui ne fournira pas un dernier caractère
0A
, bien sûr:Court et doux. Cela prend très peu de temps car il ne lit que le dernier octet (cherche à EOF). Peu importe si le fichier est gros. Ensuite, ajoutez uniquement un octet si nécessaire.
Aucun fichier temporaire nécessaire ni utilisé. Aucun lien physique n'est affecté.
Si ce test est exécuté deux fois, il n’ajoutera pas de nouvelle ligne.
la source
xxd
nehexdump
sont des utilitaires POSIX. Dans la boîte à outils POSIX, ilod -An -tx1
faut obtenir la valeur hexadécimale d'un octet.Vous feriez mieux de corriger l'éditeur de l'utilisateur qui a édité le fichier en dernier. Si vous êtes la dernière personne à avoir modifié le fichier - quel éditeur utilisez-vous, je devine textmate ..?
la source
emacs
n'ajoute pas de nouvelle ligne à la fin du fichier.(setq require-final-newline 'ask)
dans ma.emacs
Si vous souhaitez simplement ajouter rapidement une nouvelle ligne lors du traitement d'un pipeline, utilisez ceci:
il est également conforme à POSIX.
Ensuite, vous pouvez bien sûr le rediriger vers un fichier.
la source
cat file.csv | tr "\r" "\n" | { cat; echo; } | sed "/^[[:space:]]*$/d" | tail -n +2 | wc -l
Pourvu qu'il n'y ait pas de NULL en entrée:
... suffirait toujours pour ajouter une nouvelle ligne à la fin d'un infile s'il n'en avait pas déjà un. Et il suffit de lire le fichier d’entrée une seule fois pour que tout se passe bien.
la source
paste infile 1<> infile
place.Bien que cela ne réponde pas directement à la question, voici un script connexe que j'ai écrit pour détecter les fichiers qui ne se terminent pas par une nouvelle ligne. C'est très rapide.
Le script perl lit une liste de noms de fichiers (éventuellement triés) à partir de stdin et lit le dernier octet pour chaque fichier afin de déterminer si le fichier se termine par une nouvelle ligne ou non. C'est très rapide car cela évite de lire tout le contenu de chaque fichier. Il affiche une ligne pour chaque fichier lu, précédé du préfixe "erreur:" si une erreur survient, "vide:" si le fichier est vide (ne se termine pas par une nouvelle ligne!), "EOL:" ("fin de line ") si le fichier se termine par une nouvelle ligne et" no EOL: "si le fichier ne se termine pas par une nouvelle ligne.
Remarque: le script ne gère pas les noms de fichiers contenant des nouvelles lignes. Si vous êtes sur un système GNU ou BSD, vous pouvez gérer tous les noms de fichiers possibles en ajoutant -print0 pour rechercher, -z pour trier et -0 pour perl, comme ceci:
Bien sûr, il vous faudrait toujours trouver un moyen d’encoder les noms de fichiers avec des nouvelles lignes dans la sortie (à gauche comme exercice pour le lecteur).
La sortie peut être filtrée, si vous le souhaitez, pour ajouter une nouvelle ligne à ces fichiers qui n'en ont pas, le plus simplement avec
L'absence d'une nouvelle ligne peut entraîner des erreurs dans les scripts, car certaines versions de shell et d'autres utilitaires ne gèrent pas correctement une nouvelle ligne manquante lors de la lecture d'un tel fichier.
D'après mon expérience, l'absence d'un saut de ligne final est due à l'utilisation de divers utilitaires Windows pour éditer des fichiers. Je n'ai jamais vu vim causer une fin de ligne manquante lors de l'édition d'un fichier, bien qu'il en fasse rapport.
Enfin, il existe des scripts beaucoup plus courts (mais plus lents) qui peuvent suivre leurs entrées de nom de fichier pour imprimer les fichiers ne se terminant pas par une nouvelle ligne, tels que:
la source
Les éditeurs
vi
/vim
/ex
ajoutent automatiquement<EOL>
à EOF à moins que le fichier l’ait déjà.Alors essayez soit:
qui équivaut à:
Essai:
Pour corriger plusieurs fichiers, vérifiez: Comment réparer «Pas de nouvelle ligne à la fin du fichier» pour un grand nombre de fichiers? à SO
Pourquoi c'est si important? Pour garder nos fichiers compatibles POSIX .
la source
Pour appliquer la réponse acceptée à tous les fichiers du répertoire en cours (plus les sous-répertoires):
Cela fonctionne sous Linux (Ubuntu). Sous OS X, vous devez probablement utiliser
-i ''
(non testé).la source
find .
répertorie tous les fichiers, y compris les fichiers dans.git
. À exclure:find . -type f -not -path './.git/*' -exec sed -i -e '$a\' {} \;
Au moins dans les versions de GNU, son entrée est simplement
grep ''
ouawk 1
canonisée, en ajoutant une nouvelle ligne finale si elle n’est pas déjà présente. Ils copient le fichier dans le processus, ce qui prend du temps s'il est volumineux (mais le source ne devrait pas être trop volumineux pour être lu de toute façon?) Et met à jour le modtime à moins que vous ne fassiez quelque chose comme:(bien que cela puisse être correct sur un fichier que vous archivez parce que vous l'avez modifié) et qu'il perd les liens en dur, les autorisations non définies par défaut et les ACL, etc., à moins que vous ne soyez encore plus prudent.
la source
grep '' file 1<> file
, bien que cela lise et écrit le fichier complètement.Cela fonctionne sous AIX ksh:
Dans mon cas, si la nouvelle ligne manque dans le fichier, la
wc
commande retourne une valeur de2
et nous écrivons une nouvelle ligne.la source
En ajoutant à la réponse de Patrick Oscity , si vous souhaitez simplement l'appliquer à un répertoire spécifique, vous pouvez également utiliser:
find -type f | while read f; do tail -n1 $f | read -r _ || echo >> $f; done
Exécutez ceci dans le répertoire dans lequel vous souhaitez ajouter des nouvelles lignes.
la source
echo $'' >> <FILE_NAME>
ajoutera une ligne vierge à la fin du fichier.echo $'\n\n' >> <FILE_NAME>
ajoutera 3 lignes vides à la fin du fichier.la source
Si votre fichier se termine par des fins de ligne Windows
\r\n
et que vous êtes sous Linux, vous pouvez utiliser cettesed
commande. Cela ne fait qu'ajouter\r\n
à la dernière ligne si ce n'est déjà fait:Explication:
Si la dernière ligne contient déjà un,
\r\n
alors l'expression rationnelle de recherche ne correspondra pas, donc rien ne se passera.la source
Vous pouvez écrire un
fix-non-delimited-line
script comme:Contrairement à certaines des solutions données ici, il
Vous pouvez l'utiliser par exemple comme:
ou:
POSIXly, vous pourriez faire quelque chose de fonctionnellement équivalent avec
la source