Pas de nouvelle ligne à la fin du fichier

473

Lorsque vous effectuez une opération, git diffil indique "Pas de retour à la fin du fichier" .

Ok, il n'y a pas de nouvelle ligne à la fin du fichier. Quel est le problème?

Quelle est la signification du message et qu'est-ce qu'il essaie de nous dire?

Pacerier
la source
11
Peut-être, si vous avez un fichier qui se termine sans retour à la ligne et que vous ajoutez une autre ligne, git devrait montrer que l'ancienne dernière ligne a changé, car elle inclut le caractère de retour à la ligne dans la ligne?
nafg

Réponses:

458

Cela indique que vous n'avez pas de nouvelle ligne (généralement '\n', CR ou CRLF) à la fin du fichier.

Autrement dit, le dernier octet (ou les octets si vous êtes sous Windows) dans le fichier n'est pas une nouvelle ligne.

Le message s'affiche car sinon il n'y a aucun moyen de faire la différence entre un fichier où il y a une nouvelle ligne à la fin et un où il n'y en a pas. Diff doit de toute façon sortir une nouvelle ligne, sinon le résultat serait plus difficile à lire ou à traiter automatiquement.

Notez que c'est un bon style de toujours mettre la nouvelle ligne comme dernier caractère si elle est autorisée par le format de fichier. En outre, par exemple, pour les fichiers d'en-tête C et C ++, il est requis par la norme de langage.

Alexander Gladysh
la source
136
Par curiosité, pouvez-vous expliquer pourquoi il est considéré comme un bon style de toujours mettre une nouvelle ligne comme dernier caractère? Edit: a trouvé cette discussion .
Paul Bellora
84
@PaulBellora Historiquement, c'était une décision prise par le standard du langage C stackoverflow.com/a/729725/233098 Pratiquement, car de nombreux outils Unix l'exigent ou l'attendent pour un affichage correct stackoverflow.com/a/729795/233098 . Philosophiquement, parce que chaque ligne d'un fichier texte se termine par un caractère de "fin de ligne" - la dernière ligne ne devrait pas faire exception. En y réfléchissant différemment, explorons l'inverse. S'il y avait un marqueur "début de ligne" au lieu de "fin de ligne", omettriez-vous le caractère "début de ligne" sur la première ligne?
Joe
29
@Joe Cela n'a pas beaucoup de sens. Une nouvelle ligne est une nouvelle ligne , c'est-à-dire le séparateur entre les lignes, pas une fin de ligne. Nous n'avons pas de caractères de début de ligne car ils ne sont pas nécessaires. Nous n'avons pas de caractères de fin de ligne pour la même raison.
2014
6
@acjay Je soutiens qu'il y a intrinsèquement mieux entre "Séparateur entre les lignes" et "fin de ligne". Aucune de ces vues n'est intrinsèquement bonne ou mauvaise, juste une façon de voir les choses. Je propose que nous continuons d'utiliser le point de vue qui est historiquement pratique, puisque nous sommes déjà faire de cette façon et il ne du sens quand vous l' acceptez. La cohérence est importante. Il n'est pas nécessaire de casser cela au nom du point de vue "le séparateur entre les lignes".
Joe
17
@WORMSS "Nouveau pour moi" n'est pas la même chose qu'une "nouvelle convention". C'est comme découvrir tout autre type de convention de programmation. Allez-y. Vous pourriez dévier, mais vous ne faites que vous isoler. (Ou dans ce cas, briser des outils.) Pensez à combien d'autres ont découvert une convention Rails, ou PEP8, et à quel point ces communautés sont restées dans leur ensemble parce qu'elles ont cédé - malgré le code écrit au contraire.
Joe
100

Ce n'est pas seulement un mauvais style, cela peut entraîner un comportement inattendu lors de l'utilisation d'autres outils sur le fichier.

Voici test.txt:

first line
second line

Il n'y a pas de caractère de nouvelle ligne sur la dernière ligne. Voyons combien de lignes se trouvent dans le fichier:

$ wc -l test.txt
1 test.txt

C'est peut-être ce que vous voulez, mais dans la plupart des cas, vous vous attendez probablement à ce qu'il y ait 2 lignes dans le fichier.

De plus, si vous souhaitez combiner des fichiers, il se peut qu'ils ne se comportent pas comme vous vous y attendez:

$ cat test.txt test.txt
first line
second linefirst line
second line

Enfin, cela rendrait vos différences légèrement plus bruyantes si vous deviez ajouter une nouvelle ligne. Si vous ajoutez une troisième ligne, elle affichera une modification sur la deuxième ligne ainsi que le nouvel ajout.

doyen
la source
4
Le résultat de cat est correct mais le paramètre wc "-l, --lines" est tout simplement faux. Même son manuel dit "imprimer le nombre de lignes" et non "imprimer le nombre de lignes".
L'incroyable
Et je ne peux même pas reproduire cela (wc et cat) avec l'util linux récent (util-linux 2.34).
wget
1
@wget Je suis sur util-linux 2.34 et cela peut confirmer que ce que cette réponse décrit est le comportement actuel. Je suppose que votre éditeur a ajouté le caractère "\ n".
stephanos
29

La seule raison est qu'Unix avait historiquement une convention de tous les fichiers texte lisibles par l'homme se terminant par une nouvelle ligne. À l'époque, cela évitait un traitement supplémentaire lors de l'affichage ou de la jonction de fichiers texte et évitait de traiter les fichiers texte différemment des fichiers contenant d'autres types de données (par exemple, des données binaires brutes qui ne sont pas lisibles par l'homme).

En raison de cette convention, de nombreux outils de cette époque attendent la fin de la nouvelle ligne, y compris les éditeurs de texte, les outils différents et d'autres outils de traitement de texte. Mac OS X a été construit sur BSD Unix et Linux a été développé pour être compatible Unix, de sorte que les deux systèmes d'exploitation ont hérité des mêmes conventions, comportements et outils.

Windows n'a pas été développé pour être compatible Unix, il n'a donc pas la même convention, et la plupart des logiciels Windows fonctionneront très bien sans retour à la ligne.

Mais, depuis que Git a été développé pour Linux en premier, et beaucoup de logiciels open source sont construits sur des systèmes compatibles Unix comme Linux, Mac OS X, FreeBSD, etc., la plupart des communautés open source et leurs outils (y compris les langages de programmation) continuent suivre ces conventions.

Il y a des raisons techniques qui avaient du sens en 1971, mais à cette époque, c'est surtout la convention et le maintien de la compatibilité avec les outils existants.

Nathan Craike
la source
23

Si vous ajoutez une nouvelle ligne de texte à la fin du fichier existant qui n'en a pas déjà newline characterà la fin, le diff affichera l'ancienne dernière ligne comme ayant été modifiée, même si conceptuellement elle ne l'était pas.

C'est au moins une bonne raison d'ajouter un newline characterà la fin.

Exemple

Un fichier contient:

A() {
    // do something
}

Hexdump:

00000000: 4128 2920 7b0a 2020 2020 2f2f 2064 6f20  A() {.    // do 
00000010: 736f 6d65 7468 696e 670a 7d              something.}

Vous le modifiez maintenant en

A() {
    // do something
}
// Useful comment

Hexdump:

00000000: 4128 2920 7b0a 2020 2020 2f2f 2064 6f20  A() {.    // do 
00000010: 736f 6d65 7468 696e 670a 7d0a 2f2f 2055  something.}.// U
00000020: 7365 6675 6c20 636f 6d6d 656e 742e 0a    seful comment..

Le diff git affichera:

-}
\ No newline at end of file
+}
+// Useful comment.

En d'autres termes, il montre une différence plus importante que celle qui s'est produite conceptuellement. Cela montre que vous avez supprimé la ligne }et ajouté la ligne }\n. C'est en fait ce qui s'est passé, mais ce n'est pas ce qui s'est passé conceptuellement , donc cela peut être déroutant.

Jaseem
la source
2
Nous pouvons écrire la même chose dans l'autre sens: si vous supprimez une nouvelle ligne à la fin du fichier existant qui a déjà une nouvelle ligne à la fin, le diff affichera l'ancienne dernière ligne également modifiée, lorsque conceptuellement ce n'est pas le cas. Au moins une bonne raison de supprimer une nouvelle ligne à la fin.
gentiane
3
@gentiane Vous confondez "une nouvelle ligne" (une nouvelle ligne) et "une nouvelle ligne" (1 ou 2 caractères délimitant la fin d'une ligne)
minexew
@minexew Non, gentiane ne l'est pas. Peut-être que vous ne réalisez tout simplement pas qu'une "nouvelle ligne" est la même chose qu'une "nouvelle ligne".
L'incroyable
3
@TheincredibleJan La façon dont ils sont utilisés dans la réponse, les deux termes ont des significations distinctes. Je ne sais pas si vous essayez d'être un cul intelligent ou si vous comprenez simplement ce qui se passe.
minexew
18

Cela indique simplement que la fin du fichier n'a pas de nouvelle ligne. Ce n'est pas une catastrophe, c'est juste un message pour préciser qu'il n'y en a pas quand on regarde un diff dans la ligne de commande.

JohnD
la source
10

La raison pour laquelle cette convention a été mise en pratique est que sur les systèmes d'exploitation de type UNIX, un caractère de nouvelle ligne est traité comme un terminateur de ligne et / ou une limite de message (cela inclut la canalisation entre les processus, la mise en mémoire tampon de ligne, etc.).

Considérez, par exemple, qu'un fichier avec juste un caractère de nouvelle ligne est traité comme une seule ligne vide. Inversement, un fichier d'une longueur de zéro octet est en fait un fichier vide avec zéro ligne. Cela peut être confirmé selon la wc -lcommande.

Dans l'ensemble, ce comportement est raisonnable car il n'y aurait pas d'autre moyen de faire la distinction entre un fichier texte vide et un fichier texte avec une seule ligne vide si le \ncaractère était simplement un séparateur de ligne plutôt qu'un terminateur de ligne. Ainsi, les fichiers texte valides doivent toujours se terminer par un caractère de nouvelle ligne. La seule exception est si le fichier texte est destiné à être vide (pas de lignes).

Leslie Krause
la source
1
Pourquoi suis-je rétrogradé -2? J'ai souligné non seulement la confirmation de ce que d'autres réponses ont déclaré (c'est-à-dire que les outils standard basés sur UNIX attendent une nouvelle ligne comme terminateur pour les lignes), mais aussi qu'il n'y a aucun moyen de distinguer un fichier vide d'une seule ligne vide, ce qui est absolument vrai . J'ai spécifiquement répondu à la question d'origine "Quelle est la signification du message et qu'est-ce qu'il essaie de nous dire?"
Leslie Krause
Je ne vous ai pas downvote mais cette réponse semble être spécifique aux systèmes de type Unix dans la mesure où elle ne s'applique que lorsqu'une nouvelle ligne n'est que le caractère de nouvelle ligne. Il n'est pas clair que cela s'applique ici. De plus, l'avertissement semble inutile si le fichier se compose uniquement d'une ligne vide. Cependant j'évite Stackoverflow car les gens votent souvent sans explication.
user34660
9

Il y a une chose que je ne vois pas dans les réponses précédentes. Un avertissement concernant l'absence de fin de ligne peut être un avertissement lorsqu'une partie d'un fichier a été tronquée. Cela pourrait être un symptôme de données manquantes.

user34660
la source
Bon point en général, mais je ne pense pas que cela ait du sens dans le contexte de cette question particulière.
cst1992
@ cst1992 Les réponses dans Stackoverflow sont censées être aussi utiles que possible, ce qui signifie qu'elles sont censées s'appliquer à toutes les possibilités. La question est courte et je ne vois pas où elle exclut la possibilité que j'ai suggérée.
user34660
7

Le problème principal est ce que vous définissez la ligne et si la séquence de caractères de fin en ligne fait partie de la ligne ou non. Les éditeurs basés sur UNIX (tels que VIM) ou les outils (tels que Git) utilisent la séquence de caractères EOL comme terminateur de ligne, c'est donc une partie de la ligne. C'est similaire à l'utilisation du point-virgule (;) en C et Pascal. En C, le point-virgule termine les instructions, en Pascal il les sépare.

mmcorrelo
la source
4

En fait, cela pose un problème car les fins de ligne sont automatiquement modifiées les fichiers de nettoyage sans y apporter de modifications. Voir ce post pour la résolution.

git remplaçant LF par CRLF

Brian Blum
la source
3

Les fichiers sources sont souvent concaténés par des outils (C, C ++: fichiers d'en-tête, Javascript: bundlers). Si vous omettez le caractère de nouvelle ligne, vous pouvez introduire des bugs désagréables (où la dernière ligne d'une source est concaténée avec la première ligne du fichier source suivant). Espérons que tous les outils de concaténation du code source insèrent une nouvelle ligne entre les fichiers concaténés de toute façon, mais cela ne semble pas toujours être le cas.

Le nœud du problème est - dans la plupart des langues, les sauts de ligne ont une signification sémantique et la fin de fichier n'est pas une alternative définie par la langue pour le caractère de retour à la ligne. Vous devez donc terminer chaque instruction / expression par un caractère de nouvelle ligne - y compris la dernière.

Doug Coburn
la source
1
En C / C ++, vous pouvez écrire tout votre projet sur une seule ligne. Pas besoin de nouvelle ligne.
L'incroyable
Vous pouvez écrire tout votre projet sur une seule ligne ... si vous n'utilisez pas de //commentaire de style au milieu du code.
Doug Coburn
2

Votre fichier d'origine n'avait probablement pas de caractère de nouvelle ligne.

Cependant, certains éditeurs comme gedit dans linux ajoutent silencieusement une nouvelle ligne à la fin du fichier. Vous ne pouvez pas vous débarrasser de ce message lorsque vous utilisez ce type d'éditeurs.

Ce que j'ai essayé de surmonter ce problème est d'ouvrir un fichier avec l'éditeur de code Visual Studio

Cet éditeur affiche clairement la dernière ligne et vous pouvez supprimer la ligne comme vous le souhaitez.

Berkay92
la source
0

Pour ce que ça vaut, je l'ai rencontré lorsque j'ai créé un projet IntelliJ sur un Mac, puis déplacé le projet sur ma machine Windows. J'ai dû ouvrir manuellement chaque fichier et modifier le paramètre d'encodage en bas à droite de la fenêtre IntelliJ. Cela n'arrive probablement pas à la plupart des personnes qui ont lu cette question, mais cela aurait pu m'économiser quelques heures de travail ...

Lou Morda
la source