Wozu dient das Hinzufügen einer neuen Zeile am Ende einer Datei?

166

Einige Compiler (insbesondere C- oder C ++ - Compiler) geben Warnungen zu folgenden Themen aus:

No new line at end of file

Ich dachte, dies wäre ein C-Programmierer-Problem, aber github zeigt eine Meldung in der Commit-Ansicht an:

\ No newline at end of file

für eine PHP-Datei.

Ich verstehe die in diesem Thread erläuterte Präprozessor-Sache , aber was hat das mit PHP zu tun? Ist es dasselbe include()oder hat es etwas mit dem \r\nvs- \nThema zu tun ?

Was bringt es, wenn am Ende einer Datei eine neue Zeile steht?

Philipp Stephan
quelle
Duplizieren von SO: stackoverflow.com/questions/729692/…
AlikElzin-kilaka
2
Leute verärgern.
Andrew
3
Wenn Sie catdie Datei haben, wird die nächste Eingabeaufforderung an die letzte "Zeile" angehängt, wenn sie nicht mit einer neuen Zeile endet.
Aaron Franke

Antworten:

185

Es geht nicht darum, am Ende einer Datei einen zusätzlichen Zeilenumbruch einzufügen, sondern darum, den Zeilenumbruch, der vorhanden sein sollte, nicht zu entfernen.

Eine Textdatei unter Unix besteht aus einer Reihe von Zeilen , die jeweils mit einem Zeilenumbruchzeichen ( \n) enden . Eine Datei, die nicht leer ist und nicht mit einem Zeilenumbruch endet, ist daher keine Textdatei.

Dienstprogramme, die Textdateien verarbeiten sollen, kommen möglicherweise nicht mit Dateien zurecht, die nicht mit einem Zeilenumbruch enden. Beispielsweise können historische Unix-Dienstprogramme den Text nach dem letzten Zeilenumbruch ignorieren. GNU- Hilfsprogramme verhalten sich bei Nicht-Text-Dateien anständig, ebenso wie die meisten anderen modernen Hilfsprogramme. Es kann jedoch vorkommen, dass bei Dateien, denen eine letzte Zeile fehlt, ein ungewöhnliches Verhalten auftritt¹.

Wenn mit GNU diff eine der verglichenen Dateien mit einem Zeilenumbruch endet, die andere jedoch nicht, muss dies beachtet werden. Da diff zeilenorientiert ist, kann dies nicht durch Speichern einer neuen Zeile für eine der Dateien angezeigt werden, nicht jedoch für die anderen. Die neuen Zeilen sind erforderlich, um anzugeben, wo jede Zeile in der Diff-Datei beginnt und endet. Also verwendet diff diesen speziellen Text \ No newline at end of file, um eine Datei, die nicht mit einem Zeilenumbruch endet, von einer Datei zu unterscheiden, die dies tat.

Übrigens besteht eine Quelldatei in einem C-Kontext in ähnlicher Weise aus einer Reihe von Zeilen. Genauer gesagt wird eine Übersetzungseinheit in einer Implementierung betrachtet, die als eine Reihe von Zeilen definiert ist, von denen jede mit einem Zeilenumbruchzeichen enden muss ( n1256 §5.1.1.1). Auf Unix-Systemen ist das Mapping einfach. Unter DOS und Windows wird jede CR LF-Sequenz ( \r\n) einer neuen Zeile zugeordnet ( \ndies geschieht immer, wenn eine Datei gelesen wird, die unter diesen Betriebssystemen als Text geöffnet wurde). Es gibt einige Betriebssysteme, die kein Zeilenumbruchzeichen haben, sondern Datensätze mit fester oder variabler Größe. Auf diesen Systemen führt die Zuordnung von Dateien zu C-Quellen ein\nam Ende jeder Aufzeichnung. Dies ist zwar für Unix nicht direkt relevant, bedeutet aber, dass Sie beim Kopieren einer C-Quelldatei, deren letzte Zeile fehlt, auf ein System mit datensatzbasierten Textdateien und beim anschließenden Zurückkopieren eine unvollständige Datei erhalten Letzte Zeile, die bei der anfänglichen Konvertierung abgeschnitten wurde, oder eine zusätzliche Zeile, die bei der umgekehrten Konvertierung angeheftet wurde.

¹ Beispiel: Die Ausgabe von GNU sort endet immer mit einem Zeilenumbruch. Wenn der Datei foodie letzte Zeile fehlt, werden Sie feststellen, dass sie sort foo | wc -cein Zeichen mehr als enthält cat foo | wc -c.

Gilles
quelle
Betreffs "... einer Reihe von Zeilen, von denen jede mit einem Zeilenumbruchzeichen enden muss (n1256 §5.1.1.1)" -> Beim erneuten Betrachten eines neueren C11dr N1570 wurde keine andere Unterstützung gefunden als vielleicht: "Eine Quelldatei, die nicht leer ist, muss mit einem Zeilenumbruch enden, dem vor dem Spleißen kein umgekehrter Schrägstrich vorangestellt werden darf." §5.1.1.2 2, aber das scheint auf Spleißspezifikationen beschränkt zu sein.
Chux
@chux Dieser Satz ist auch in n1256 vorhanden. Die letzte Zeile muss mit einem Zeilenumbruchzeichen enden. Zeilen, die nicht die letzte Zeile sind, müssen natürlich auch mit einem Zeilenumbruchzeichen enden, um anzuzeigen, dass diese Zeile endet und die nächste Zeile beginnt. Daher muss jede Zeile mit einem Zeilenumbruchzeichen enden.
Gilles
Hmmm, für mich könnte diese Zeile "" Eine Quelldatei ... Spleißen findet statt. "Sich darauf beschränken, wie Überlegungen zum Spleißen und nicht auf Dateien im Allgemeinen. Dennoch sehe ich, wie man etwas anderes anzeigen kann. Vielleicht suche ich einen Beitrag das konzentriert sich auf das.
Chux
> "Also verwendet diff diesen speziellen Text \ No newline at end of file, um eine Datei, die nicht in einer Newline endet, von einer Datei zu unterscheiden, die dies getan hat." Git zeigt diesen Text nicht nur beim Vergleichen von Dateien. Aber auch wenn git eine neue Datei hinzufügt. Ich nehme an, dieses Argument ist ungültig.
Viktor Kruglikov
> "Dienstprogramme, die mit Textdateien arbeiten sollen, kommen möglicherweise nicht mit Dateien zurecht, die nicht mit einem Zeilenumbruch enden" Ich glaube nicht, dass es eine Sache von git ist, sich um so einfache Probleme wie das Fehlen von \ n aufgrund von POSIX zu kümmern Anforderungen. Ich denke, wenn git diese Meldung anzeigt, sollte der Grund in Problemen mit der Quellcodeverwaltung liegen .
Viktor Kruglikov
41

Nicht unbedingt der Grund, aber eine praktische Konsequenz von Dateien, die nicht mit einer neuen Zeile enden:

Überlegen Sie, was passieren würde, wenn Sie mehrere Dateien mit verarbeiten möchten cat. Wenn Sie beispielsweise das Wort fooam Zeilenanfang in drei Dateien suchen möchten:

cat file1 file2 file3 | grep -e '^foo'

Beginnt die erste Zeile in Datei3 mit foo, aber Datei2 hat \nnach der letzten Zeile kein Finale , würde dieses Vorkommen von grep nicht gefunden, da die letzte Zeile in Datei2 und die erste Zeile in Datei3 von grep als eine einzige Zeile angesehen würden Linie.

Aus Gründen der Konsistenz und um Überraschungen zu vermeiden, versuche ich, meine Dateien immer mit einer neuen Zeile zu versehen.

Sergio Acosta
quelle
Aber ist es Sache von Git, sich um die Verkettung von Dateien zu kümmern?
Viktor Kruglikov
Ist es nicht naheliegend, dass Sie einfach '\n'die Katzenoperation durchführen ...
Andrew,
3
Das ist so, als würde man sagen: "Manchmal füge ich Strings mit \noder ohne Leerzeichen an den Enden zusammen. Um die Dinge konsistent zu halten, setze ich immer \n _____beide Enden meiner Strings an." Nun, nein, es ist das Richtige, wenn Sie Ihre Strings zuschneiden und dann richtig verketten.
Andrew
16

Es gibt zwei Aspekte:

  1. Es gibt / gab einige C-Compiler, die die letzte Zeile nicht analysieren können, wenn sie nicht mit einer neuen Zeile endet. Der C-Standard legt fest, dass eine C-Datei mit einer neuen Zeile (C11, 5.1.1.2, 2.) enden soll und dass eine letzte Zeile ohne neue Zeile undefiniertes Verhalten ergibt (C11, J.2, 2. Punkt). Vielleicht aus historischen Gründen, weil ein Hersteller eines solchen Compilers bei der Erstellung des ersten Standards Teil des Komitees war. So die Warnung von GCC.

  2. diffProgramme (wie sie von git diff, github usw. verwendet werden) zeigen zeilenweise Unterschiede zwischen Dateien an. Sie drucken normalerweise eine Nachricht, wenn nur eine Datei mit einem Zeilenumbruch endet, da Sie sonst diesen Unterschied nicht sehen würden. Zum Beispiel , wenn der einzige Unterschied zwischen zwei Dateien die Anwesenheit des letzten Newline - Zeichens ist, ohne den Hinweis darauf , wie die beiden Dateien waren die gleiche, wenn aussehen würde diffund cmpgibt einen austritt Code ungleich Erfolg und die Prüfsummen der Dateien (zB über md5sum) stimmen nicht überein.

maxschlepzig
quelle
Sinn machen mit diff-Programm
Thamaraiselvam
Klingt wie Diffs sollte nur schlauer sein.
Andrew
@ Andrew, nein, tut es nicht. diffEs wird erwartet, dass Differenzen gedruckt werden, wenn welche vorhanden sind. Und wenn eine Datei eine neue Zeile als letztes Zeichen hat, während die andere keine hat, muss dieser Unterschied in der Ausgabe irgendwie spürbar sein.
maxschlepzig
Ihre letztere Aussage ist richtig. Der Diff-Viewer muss jedoch nicht zuerst "newlines" ( \n) anzeigen , sondern kann stattdessen einfach "new lines" anzeigen .
Andrew
10

Das, was \ No newline at end of fileSie von Github erhalten , wird am Ende eines Patches angezeigt (im diffFormat siehe den Hinweis am Ende des Abschnitts "Einheitliches Format").

Es ist den Compilern egal, ob sich am Ende einer Datei ein Zeilenumbruch befindet oder nicht, diese müssen jedoch git(und die diff/ patchutilities) berücksichtigen. Dafür gibt es viele Gründe. Wenn Sie beispielsweise vergessen, eine neue Zeile am Ende einer Datei hinzuzufügen oder zu entfernen, ändert sich deren Hashsumme ( md5sum/ sha1sum). Dateien sind auch nicht immer Programme, und ein Final \nkann einen Unterschied machen.

Hinweis : Wegen der Warnung von C-Compilern bestehen sie vermutlich aus Gründen der Abwärtskompatibilität auf einer abschließenden neuen Zeile. Sehr alte Compiler akzeptieren möglicherweise nicht die letzte Zeile, wenn sie nicht mit \n(oder einer anderen systemabhängigen Zeichenfolge für das Zeilenende) enden.

Stéphane Gimenez
quelle
7
„Ich glaube , sie bestehen darauf , für eine abschließende Newline für Abwärtskompatibilität“ - Nein, bestehen sie es auf , weil die C - Standard Mandate es.
MestreLion
1
@MestreLion C benötigt eine letzte neue Zeile für C-Quellcode (C11 §5.1.1.2 2). Beachten Sie, dass für die Textdatei- E / A C Folgendes hat: "Ob für die letzte Zeile ein abschließendes Zeichen für eine neue Zeile erforderlich ist, ist implementierungsspezifisch." §7.21.2 2
chux
Wer benutzt sehr alte Compiler? Hör auf, sie zu benutzen.
Andrew
1
@ MestreLion: Und warum, glauben Sie, schreibt der C-Standard dies vor?
Stéphane Gimenez,
@ StéphaneGimenez: Konsistenz, bessere Kompatibilität und Interoperabilität zwischen verschiedenen Betriebssystemen (POSIX definiert auch Zeilen, die mit '\ n' enden)
MestreLion
4

POSIX: Hierbei handelt es sich um eine Reihe von Standards, die von IEEE festgelegt wurden, um die Kompatibilität zwischen Betriebssystemen zu gewährleisten.

Eine davon ist die Definition einer "Zeile", die eine Folge von null oder mehr Nichtzeichen plus einem abschließenden Zeilenumbruchzeichen ist.

Damit diese letzte Zeile als tatsächliche "Zeile" erkannt wird, sollte sie ein abschließendes Zeichen für eine neue Zeile enthalten.

Dies ist wichtig, wenn Sie von OS-Tools abhängig sind, um die Zeilenanzahl zu bestimmen oder Ihre Datei zu teilen / zu analysieren. Angesichts der Tatsache, dass PHP eine Skriptsprache ist, ist dies durchaus möglich, insbesondere in den frühen Tagen oder sogar jetzt (ich habe keine Ahnung / Postulierung), da es solche Betriebssystemabhängigkeiten hatte.

In der Realität sind die meisten Betriebssysteme nicht vollständig POSIX-konform und der Mensch ist nicht so maschinenfreundlich oder kümmert sich auch nicht darum, neue Leitungen zu terminieren. Für die meisten Dinge ist es also ein Smorgasbord von allem, das sich entweder darum kümmert, warnt oder nur das letzte Stück Text enthält, also füge es einfach ein.

user3379747
quelle
3

Es ist auch der Punkt, diff Geschichte zu halten. Wenn eine Datei ohne ein Zeilenumbruchzeichen endet, wird das Hinzufügen von Elementen am Ende der Datei von diff-Dienstprogrammen als Änderung der letzten Zeile angesehen (da \nsie hinzugefügt wird).

Dies kann zu unerwünschten Ergebnissen bei Befehlen wie git blameund führen hg annotate.

Hosam Aly
quelle
Klingt wie Unterschiede müssen nur schlauer sein.
Andrew