Fügt vi am Ende der Datei stillschweigend eine neue Zeile (LF) hinzu?

Fügt vi am Ende der Datei stillschweigend eine neue Zeile (LF) hinzu?

Ich habe Probleme, ein seltsames Verhalten zu verstehen: vi scheint eine neue Zeile hinzuzufügen (ASCII: LF, da es sich um ein Unix handelt (AIX)-System) am Ende der Datei, obwohl ich es NICHT ausdrücklich eingegeben habe.

Ich bearbeite die Datei in vi folgendermaßen (und achte dabei darauf, am Ende keinen Zeilenumbruch einzufügen):

# vi foo   ## Which I will finish on the char "9" and not input a last newline, then `:wq`
123456789
123456789
123456789
123456789
~
~
  ## When I save, the cursor is just above the last "9", and no newline was added.

Ich erwarte, dass vi es „so wie es ist“ speichert, also 39 Bytes hat: 10 ASCII-Zeichen in jeder der ersten drei Zeilen (Zahlen 1 bis 9, gefolgt von einem Zeilenumbruch (LF auf meinem System)) und nur 9 in der letzten Zeile (Zeichen 1 bis 9, kein abschließender Zeilenumbruch/LF).

Aber wenn ich es speichere, scheint es so zu sein40Bytes (anstelle von 39) undod zeigt ein abschließendes LF:

# wc foo
       4       4      40 foo  ## I expected 39 here! as I didn't add the last newline
# od -a toto
0000000    1   2   3   4   5   6   7   8   9  lf   1   2   3   4   5   6
0000020    7   8   9  lf   1   2   3   4   5   6   7   8   9  lf   1   2
0000040    3   4   5   6   7   8   9  lf
0000050
     ## An "lf" terminates the file?? Did vi add it silently?

Wenn ich die Datei mit einem printf erstelle und genau das mache, was ich in vi gemacht habe, funktioniert es wie erwartet:

# ## I create a file with NO newline at the end:
# printf "123456789\n123456789\n123456789\n123456789" > foo2
# wc foo2  ## This one is as expected: 39 bytes, exactly as I was trying to do above with vi.
       3       4      39 foo  ## As expected, as I didn't add the last newline

  ## Note that for wc, there are only three lines!
  ## (So wc -l doesn't count lines; it counts the [newline] chars... Which is rather odd.)

# root@SPU0WMY1:~  ## od -a foo2
0000000    1   2   3   4   5   6   7   8   9  lf   1   2   3   4   5   6
0000020    7   8   9  lf   1   2   3   4   5   6   7   8   9  lf   1   2
0000040    3   4   5   6   7   8   9
0000047                                ## As expected, no added LF.

Beide Dateien (foo (40 Zeichen) und foo2 (39 Zeichen)) sehen genau gleich aus, wenn ich sie erneut mit vi öffne ...

Und wenn ich foo2 (39 Zeichen, kein abschließender Zeilenumbruch) in vi öffne undmachen Sie es einfach :wq, ohne es zu bearbeiten, es heißt, es schreibt 40 Zeichen und der Zeilenvorschub wird angezeigt!

Ich kann nicht auf ein neueres vi zugreifen (ich mache das auf AIX, vi (nichtVim) Version 3.10, glaube ich? (keine „-version“ oder andere Möglichkeit, dies herauszufinden)).

# strings /usr/bin/vi | grep -i 'version.*[0-9]'
@(#) Version 3.10

Ist es bei vi normal (und vielleicht nicht bei neueren Versionen? Oder Vim?), stillschweigend eine neue Zeile am Ende einer Datei einzufügen? (Ich dachte, das ~ zeigt an, dass die vorherige Zeile NICHT mit einer neuen Zeile endete.)

--

Bearbeiten:einige zusätzliche Aktualisierungen und eine kleine Zusammenfassung, mit großem Dank für die Antworten unten:

  • vi fügt stillschweigend eine abschließende neue Zeile hinzu, wenn es eine Datei schreibt, in der dies fehlte (es sei denn, die Datei ist leer).

  • dies geschieht nur beim Schreiben! (d. h. bis Sie :w verwenden, können Sie :e verwenden, um zu überprüfen, ob die Datei noch so ist, wie Sie sie geöffnet haben ... (d. h.: es wird immer noch „Dateiname“ [Letzte Zeile ist nicht vollständig] N Zeile, M Zeichen angezeigt). Wenn Sie speichern, wird stillschweigend und ohne spezielle Warnung eine neue Zeile hinzugefügt (es wird zwar angegeben, wie viele Bytes gespeichert werden, aber dies reicht in den meisten Fällen nicht aus, um zu wissen, dass eine neue Zeile hinzugefügt wurde) (danke an @jiliagre, der mit mir über die öffnende vi-Nachricht gesprochen hat, es hat mir geholfen, einen Weg zu finden, um zu wissen, wann die Änderung wirklich auftritt)

  • Dies (stille Korrektur) istPOSIXVerhalten! (siehe @barefoot-io-Antwort für Referenzen)

Antwort1

POSIX erfordert dieses Verhalten, es ist also keineswegs ungewöhnlich.

Von demPOSIX vi-Handbuch:

Eingabedateien

Eine Beschreibung der vom Befehl vi unterstützten Eingabedateien finden Sie im Abschnitt EINGABEDATEIEN des Befehls ex.

Folgen Sie der Spur zumPOSIX ex-Handbuch:

Eingabedateien

Eingabedateien müssen Textdateien oder Dateien sein, die Textdateien wären, wenn die letzte Zeile nicht vollständig wäre, nicht länger als {LINE_MAX}-1 Byte ist und keine NUL-Zeichen enthält. Standardmäßig wird jede unvollständige letzte Zeile so behandelt, als ob sie ein abschließendes <newline> hätte. Das Bearbeiten anderer Dateiformate kann optional durch Ex-Implementierungen zugelassen werden.

Der Abschnitt „AUSGABEDATEIEN“ des vi-Handbuchs leitet auch zum Beispiel weiter:

AUSGABEDATEIEN

Die Ausgabe von ex soll eine Textdatei sein.

Ein Paar POSIX-Definitionen:

3.397 Textdatei

Eine Datei, die Zeichen enthält, die in null oder mehr Zeilen organisiert sind. Die Zeilen enthalten keine NUL-Zeichen und keine darf eine Länge von {LINE_MAX} Bytes überschreiten, einschließlich des Zeichens <newline>. Obwohl POSIX.1-2008 nicht zwischen Textdateien und Binärdateien unterscheidet (siehe ISO-C-Standard), erzeugen viele Dienstprogramme nur dann vorhersehbare oder sinnvolle Ausgaben, wenn sie mit Textdateien arbeiten. Die Standard-Dienstprogramme, die solche Einschränkungen haben, geben in ihren Abschnitten STDIN oder INPUT FILES immer „Textdateien“ an.

3.206 Linie

Eine Folge von null oder mehr Nicht-<newline>-Zeichen plus einem abschließenden <newline>-Zeichen.

Diese Definitionen im Kontext dieser Handbuchseitenauszüge bedeuten, dass eine konforme ex/vi-Implementierung zwar eine fehlerhafte Textdatei akzeptieren muss, wenn die einzige Fehlerhaftigkeit dieser Datei ein fehlender abschließender Zeilenumbruch ist, das Ergebnis beim Schreiben des Puffers dieser Datei jedoch eine gültige Textdatei sein muss.

Dieser Beitrag bezieht sich zwar auf die Ausgabe 2013 des POSIX-Standards,die entsprechenden Bestimmungen finden sich auch in der wesentlich älteren Ausgabe von 1997.

Und schließlich: Wenn Sie die Newline-Anfügung von „ex“ unerwünscht finden, werden Sie sich durch die intolerante Ausgabe der 7. UNIX-Edition (1979) zutiefst verletzt fühlen.Aus dem Handbuch:

Beim Lesen einer Datei verwirft ed ASCII-NUL-Zeichen und alle Zeichen nach dem letzten Zeilenumbruch. Dateien mit Nicht-ASCII-Zeichen werden nicht gelesen.

Antwort2

Dies ist das erwartete viVerhalten.

Die letzte Zeile Ihrer Datei ist unvollständig, daher handelt es sich strenggenommen (also gemäß POSIX-Standard) nicht um eine Textdatei, sondern um eine Binärdatei.

viDabei handelt es sich um einen Textdatei-Editor, nicht um einen Binäreditor, der das Problem beim Speichern problemlos behebt.

Dadurch können andere Textdateitools wie wcund sedähnliche die erwartete Ausgabe liefern. Beachten Sie, dass vidas Problem nicht verschwiegen wird:


$ printf "one\ntwo" >file     # Create a unterminated file
$ cat file                    # Note the missing newline before the prompt
one
two$ wc -l file               # wc ignores the incomplete last line
       1 file
$ sed '' file > file1
$ cat file1                   # so does a legacy sed
one
$ PATH=$(getconf PATH) sed  '' file
one                           # while a POSIX conformant sed warns you:
sed: Missing newline at end of file file.
two
$ vi file
one
two
~
~
~                             # vi tells you too about the issue
"file" [Incomplete last line] 2 lines, 7 characters

:w

"file" 2 lines, 8 characters  # and tells it writes two lines
                              # You'll even notice it writes one more
                              # character if you are a very shrewd observer :-)
:q
$ cat file                    # the file is now valid text
one
two
$ wc -l file                  # wc reports the expected number of lines
       2 file
$ sed '' file > file1         # sed works as expected
$ cat file1
one
two

Hinweis: Um Hinweise zu erhalten, welche viVersion Sie verwenden, können Sie den :vefolgenden Befehl verwenden. Hier wird angezeigt, dass ich hier eine ältere SVR4-Version verwende, definitiv nicht vim:

:ve
Version SVR4.0, Solaris 2.5.0

In Ihrem steht offenbar:

:ve
Version 3.10

Das bedeutet wahrscheinlich, dass AIX viauf SVR3-Quellcode basiert.

[Incomplete last line]Dieses Verhalten und die Warnmeldung sind auf jeden Fall viseit mindestens 1979 im Quellcode von Bill Joy enthalten und meines Wissens nach in allen Zweigen erhalten geblieben, die aus den Quellcodeversionen von System V erstellt wurden, auf denen proprietäre Unix-Systeme wie AIX basieren.

Chronologisch gesehen ist dieses Verhalten keine Folge der POSIX-Konformität, sondern eher eine Folge von Bill Joys ursprünglicher Entscheidung, Benutzern beim Bearbeiten gefälschter Textdateien behilflich zu sein, und dann, ein Jahrzehnt später, der Entscheidung des POSIX-Komitees, diese Toleranz beizubehalten.

Wenn Sie edanstelle von verwenden vi, werden Sie feststellen, dass Ersteres ausführlicher zu dem Problem berichtet, zumindest wenn Ihr edQuellcode aus SVR3 oder einem neueren Zweig stammt:

$ ed file
'\n' appended
8
q

Beachten Sie auch, dass eine leere Datei eine gültige Textdatei ist, die zufällig null Zeilen enthält. Da dann keine nicht beendete Zeile zu korrigieren ist, viwird beim Speichern der Datei kein Zeilenumbruch angehängt.

Antwort3

Wenn Text, dem fälschlicherweise der letzte Zeilenumbruch fehlt, durch eine Shell- whileSchleife ausgeführt wird, wird die letzte Zeile stillschweigend verworfen.

$ (echo transaction 1; echo -n transaction 2) \
  | while read line; do echo $line; done
transaction 1
$ 

Sicherzustellen, dass es eine letzte Zeile gibt, ist die richtige und vernünftige Standardeinstellung. Die andere Möglichkeit besteht darin, den gesamten Shell-Code zu kennen und die Zeit dafür aufzuwenden, wenn er mit Texten in Berührung kommt, denen die letzte Zeile fehlt, oder das Risiko einzugehen, die letzte Textzeile zu verlieren.

Antwort4

Ich kann mich an kein anderes Verhalten erinnern, bei dem am Ende einer Datei eine neue Zeile hinzugefügt wird (wird viseit Mitte der 80er Jahre verwendet).

Das ~zeigt an, dass eine Zeile auf dem Bildschirm nicht Teil des Textes ist, nicht dass die Datei nicht mit einem Zeilenumbruch endet. (Es kann schwierig werden, Fehler aufzuspüren, wenn Sie ein ~in die letzte Zeile von Shell-Skripten setzen.) Wenn Sie eine kurze Datei mit einem Zeilenumbruch am Ende laden, werden Sie das ~selbst sehen und Ihre Annahme widerlegen, dass es auf Text hinweist, der nicht mit einem Zeilenumbruch endet.

verwandte Informationen