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

36

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

Ich bearbeite die Datei als solche in vi (achte darauf, dass du am Ende keine neue Zeile eingibst):

# 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, um 39 Bytes zu haben: 10 ASCII-Zeichen in jeder der ersten drei Zeilen (Nummern 1 bis 9, gefolgt von einer neuen Zeile (LF auf meinem System)) und nur 9 in der letzten Zeile (Zeichen 1 bis 9, kein abschließender Zeilenumbruch / LF).

Aber es erscheint, wenn ich es speichere, es ist 40 Bytes (anstelle von 39), und od 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, der genau das tut, was ich in vi getan 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 genauso aus, wenn ich sie mit vi ...

Und wenn ich foo2 (39 Zeichen, kein abschließender Zeilenumbruch) in vi öffne und einfach auf die :wqBearbeitung verzichte, werden 40 Zeichen geschrieben und der Zeilenvorschub wird angezeigt!

Ich kann nicht auf ein neueres vi zugreifen (ich mache das unter AIX, vi (nicht Vim ) Version 3.10, glaube ich? (Keine "-version" oder andere Mittel, um es zu wissen)).

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

Ist es normal, dass vi (und möglicherweise nicht in einer neueren Version? Oder Vim?) Am Ende einer Datei stillschweigend eine neue Zeile hinzufügt? (Ich dachte, das ~ deutet darauf hin, dass die vorherige Zeile NICHT mit einer neuen Zeile endet.)

-

Bearbeiten: einige zusätzliche Updates und eine kleine Zusammenfassung, mit großem Dank an die Antworten unten:

  • vi Fügen Sie stillschweigend eine abschließende Zeile hinzu, wenn eine Datei geschrieben wird, der diese fehlte (es sei denn, die Datei ist leer).

  • Dies geschieht nur zum Zeitpunkt des Schreibens! (dh, bis Sie: w, können Sie: e verwenden, um zu überprüfen, ob die Datei noch in dem Zustand ist, in dem Sie sie geöffnet haben ... (dh, es wird immer noch "Dateiname" angezeigt. [Letzte Zeile ist nicht vollständig.] N Zeile, M Zeichen). Wenn Sie speichern, wird eine neue Zeile stillschweigend ohne besondere Warnung hinzugefügt (es wird zwar angegeben, wie viele Bytes gespeichert werden, dies reicht jedoch in den meisten Fällen nicht aus, um zu wissen, dass eine neue Zeile hinzugefügt wurde) (danke an @jiliagre, dass Sie mit mir über das gesprochen haben Nachdem ich die vi-Nachricht geöffnet habe, habe ich herausgefunden, wann die Änderung tatsächlich eintritt.

  • Dies (stille Korrektur) ist POSIX- Verhalten! (Referenzen finden Sie unter @ barefoot-io answer)

Olivier Dulac
quelle
Nur der Vollständigkeit halber, welche Version von AIX (Vollversion).
EightBitTony
2
Mir ist nicht bekannt, dass AIX vi diese Option hat - nur für Vim
Jeff Schaller
1
@ JeffSchaller: Danke für den Link. Leider native vi hat nicht ": set noeol" noch die Option -b im Binärmodus zu öffnen ...
Olivier Dulac
1
Möglicherweise können Sie die viVersion oder zumindest einen Hinweis auf die Herkunft erhalten, indem Sie den :veBefehl ausführen.
Juli
1
@ ThomasDickey In der Tat. Aus irgendeinem Grund hat IBM die exHandbuchseite entfernt, auf der der :verBefehl normalerweise dokumentiert ist.
Juli

Antworten:

28

Dies ist das erwartete viVerhalten.

Ihre Datei enthält eine unvollständige letzte Zeile, so dass es sich streng genommen (dh gemäß dem POSIX-Standard) nicht um eine Textdatei, sondern um eine Binärdatei handelt.

vi Dies ist ein Textdatei-Editor, kein Binär-Editor. Er behebt ihn ordnungsgemäß, wenn Sie ihn speichern.

Dies ermöglicht es anderen Textdatei-Tools und dergleichen wc, seddie erwartete Ausgabe bereitzustellen. Beachten Sie, dass vizu diesem Thema nicht geschwiegen 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

Beachten viSie, dass Sie den :veBefehl verwenden können , um einige Hinweise zu erhalten, welche Version Sie ausführen. Es zeigt, dass ich hier einen älteren SVR4 verwende, definitiv nicht vim:

:ve
Version SVR4.0, Solaris 2.5.0

Anscheinend heißt es bei Ihnen:

:ve
Version 3.10

viDies bedeutet wahrscheinlich, dass AIX auf SVR3-Quellcode basiert.

In jedem Fall sind dieses Verhalten und die [Incomplete last line]Warnmeldung viseit mindestens 1979 und AFAIK im früheren Quellcode von Bill Joy enthalten und in allen Zweigen enthalten, die mit System V-Quellcode-Releases erstellt wurden, aus denen proprietäre Unix-Versionen wie AIX erstellt wurden.

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

Wenn Sie edanstelle von verwenden vi, werden Sie feststellen, dass das erstere ausführlicher über das Problem ist, zumindest wenn edes aus SVR3 oder einem neueren Quellenzweig stammt:

$ ed file
'\n' appended
8
q

Beachten Sie auch, dass eine leere Datei eine gültige Textdatei ist, die zufällig Nullzeilen enthält. Da dann keine nicht abgeschlossene Zeile zu reparieren ist, viwird beim Speichern der Datei keine neue Zeile angehängt.

jlliagre
quelle
1
Ich glaube, Sie vim für vi verwechseln;) Vermächtnis vi ist sehr viel weniger wortreich als dies ...
Olivier Dulac
@OlivierDulac Ich verwirre sie nicht. Dieser Test wurde mit dem SVR4-Legacy durchgeführt, vigenau wie das OP, obwohl auf einem anderen Unix. Dies ist nicht der eine vimoder andere Klon. Antwort aktualisiert, um dies zu klären.
Juli
@OlivierDulac Hmm, mir ist gerade aufgefallen, dass du eigentlich der OP bist. Es scheint, dass AIX eine ältere System V-Verzweigung für die viImplementierung verwendet. Möglicherweise SVR3. Sind Sie sicher, dass [Incomplete last line]beim Öffnen der Datei keine Meldung angezeigt wird?
Juli
@OlivierDulac Dieser Link scheint zu implizieren, dass dieselbe Meldung von der AIX- viImplementierung angezeigt werden kann : www-01.ibm.com/support/docview.wss?uid=isg1IZ27694
jlliagre
Ich werde versuchen, das morgen zu sehen
Olivier Dulac
51

POSIX erfordert dieses Verhalten, es ist also in keiner Weise ungewöhnlich.

Aus dem POSIX vi-Handbuch :

EINGABEDATEIEN

Im Abschnitt INPUT FILES des Befehls ex finden Sie eine Beschreibung der vom Befehl vi unterstützten Eingabedateien.

Dem Weg zum POSIX ex-Handbuch folgen :

EINGABEDATEIEN

Eingabedateien müssen Textdateien oder Textdateien sein, mit Ausnahme einer unvollständigen letzten Zeile, die 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 hätte sie eine nachfolgende <neue Zeile>. Das Bearbeiten anderer Dateiformate kann optional durch Ex-Implementierungen ermöglicht werden.

Der Abschnitt OUTPUT FILES des vi-Handbuchs leitet auch Folgendes weiter:

AUSGABEDATEIEN

Die Ausgabe von ex sollen Textdateien sein.

Ein Paar POSIX-Definitionen:

3.397 Textdatei

Eine Datei, die Zeichen enthält, die in null oder mehr Zeilen angeordnet sind. Die Zeilen enthalten keine NUL-Zeichen und keine darf länger als {LINE_MAX} Byte sein, einschließlich des Zeichens <newline>. Obwohl POSIX.1-2008 nicht zwischen Textdateien und Binärdateien unterscheidet (siehe ISO C-Standard), liefern viele Dienstprogramme nur eine vorhersehbare oder aussagekräftige Ausgabe, wenn sie mit Textdateien arbeiten. Die Standarddienstprogramme, für die solche Einschränkungen gelten, geben in ihren Abschnitten STDIN oder INPUT FILES immer "Textdateien" an.

3.206 Line

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 eine fehlerhafte Textdatei akzeptieren muss, wenn die einzige Deformität dieser Datei eine fehlende letzte Zeile ist. Beim Schreiben des Puffers dieser Datei muss das Ergebnis eine gültige Textdatei sein.

Während in diesem Beitrag auf die Ausgabe 2013 des POSIX-Standards verwiesen wurde, erscheinen die entsprechenden Bestimmungen auch in der viel älteren Ausgabe von 1997 .

Wenn Sie die Newline Appension von Ex als unerwünscht empfinden, werden Sie sich von der intoleranten Ausgabe von Seventh Edition UNIX (1979) zutiefst verletzt fühlen. Aus dem Handbuch :

Beim Lesen einer Datei werden ASCII-NUL-Zeichen und alle Zeichen nach dem letzten Zeilenumbruch verworfen. Es lehnt es ab, Dateien zu lesen, die Nicht-ASCII-Zeichen enthalten.

Barfuß IO
quelle
danke, das beantwortet meine frage. Ich werde nur noch ein paar Tage warten, falls sich eine bessere Antwort ergibt, aber im Moment bin ich der Meinung, dass Sie die akzeptierte Antwort sein können.
Olivier Dulac
Sehr gut gemacht auf die gründlich dokumentierte Antwort, direkt von den Spezifikationen! :)
Wildcard
1
@Wildcard, das Verhalten ging jedoch den Spezifikationen voraus.
Juli
@jlliagre, es sei denn, Sie haben ein Memoir von Bill Joy oder vielleicht dem Schöpfer von ex(weiß nicht, wie er heißt), ich denke, POSIX-Spezifikationen sind so gut wie erwartet. ;) Bis zu diesem Punkt der "Originalquelle" am nächsten, obwohl es wahr ist, begannen sie als mehr oder weniger Beschreibung der vorhandenen Funktionalität.
Wildcard
3
@Wildcard exwurde von Bill Joy und Chuck Alley ( web.cecs.pdx.edu/~kirkenda/joy84.html ) mitgeschrieben . Ich stelle die POSIX-Spezifikationen nicht in Frage und die Tatsache, dass aktuelle viReleases folgen, ich erkläre nur das Verhalten lange älter als es ist.
Juli
1

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

Das ~bedeutet, dass eine Zeile auf dem Bildschirm nicht Teil des Texts ist und die Datei nicht in einer neuen Zeile endet. (Es kann schwierig werden, Fehler zu verfolgen, wenn Sie in ~die letzte Zeile der Shell-Skripte ein setzen.) Wenn Sie eine kurze Datei mit einem Zeilenumbruch am Ende laden, werden Sie sich ~selbst sehen und beweisen, dass Ihr Gedanke darauf hindeutet, dass der Text nicht mit Zeilenumbruch endet.

Anthon
quelle
Was mich überrascht, ist die Hinzufügung einer neuen Zeile ... Ich erwarte, dass vi sie nicht stillschweigend hinzufügt, aber es scheint so ... Ich suche nach einer Erklärung für diese Einstellung (die beunruhigende Tatsache ist: Ich öffne foo2 (ohne hinter LF) und nur: wq, es ändert seinen Inhalt ... so zeigt es mir etwas, speichert aber eine andere Sache ... seltsam, um es gelinde auszudrücken ^^
Olivier Dulac
In seinem Vorgänger ( ed) würden Sie Linien erstellen und diese bearbeiten, nicht durch Anhängen von Zeichen. Ich habe immer auch an vi als zeilenorientierten Editor gedacht. Aber ich verstehe deine Überraschung.
Anthon
1

Text, dem die ultimative neue Zeile durch eine Shell- whileSchleife nicht ordnungsgemäß fehlt, führt dazu, dass die letzte Zeile stillschweigend verworfen wird.

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

Sicherzustellen, dass es einen endgültigen Zeilenumbruch gibt, ist die richtige und vernünftige Vorgabe. Die andere Möglichkeit besteht darin, zu wissen und die Zeit zu haben, den gesamten Shell-Code zu prüfen, der Text berührt, dem die letzte Zeile fehlt, oder zu riskieren, die letzte Zeile des Texts zu verlieren.

Thrig
quelle