web-dev-qa-db-fra.com

VI a-t-il ajouté silencieusement une nouvelle ligne (LF) à la fin du fichier?

J'ai du mal à comprendre un comportement étrange: VI semble ajouter une nouvelle ligne (ASCII: LF, car il s'agit d'un système ( [~ # ~ # ~] aix [~ # ~] ) à la fin de le fichier, quand je ne le sais pas spécifiquement.

Je modifie le fichier comme tel dans VI (en prenant soin de ne pas saisir une nouvelle ligne à la fin):

# 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.

Je m'attends à ce que VI pour l'enregistrer "tel quel", donc d'avoir 39 octets: 10 ASCII caractères sur chacune des trois premières lignes (numéros 1 à 9, suivi d'une nouvelle ligne (LF sur mon Système))) et seulement 9 sur la dernière ligne (caractères 1 à 9, aucune résiliation Newline/LF).

Mais il apparaît lorsque je le sauve que c'est 40 octets (au lieu de 39), et OD montre une résiliation SLF :

# 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?

Si je crée le fichier avec un imprimeur faisant exactement ce que j'ai fait à l'intérieur de VI, cela fonctionne comme prévu:

# ## 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.

Les deux fichiers (FOO (40 caractères) et FOO2 (39 caractères) apparaissent exactement les mêmes si je les ouvre avec VI ...

Et si j'ouvre FOO2 (39 caractères, aucune ligne de terminaison newline) dans VI et juste do :wq Sans le montage de ce que ce soit , il est dit qu'il écrit 40 caractères et la surface de ligne apparaît!

Je ne peux pas avoir accès à un VI plus récent (je le fais sur AIX, VI (pas VIM ) Version 3.10 Je pense? (Pas de "-version" ou autre moyen de le savoir)).

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

Est-ce normal pour VI (et peut-être pas dans une version plus récente? Ou Vim?) Ajouter silencieusement une nouvelle ligne à la fin d'un fichier? (Je pensais que le ~ indiqué que la ligne précédente ne s'est pas terminée par une nouvelle ligne.)

-

Edit: Quelques mises à jour supplémentaires et un peu de résumé, avec un grand remerciement aux réponses ci-dessous:

  • vI Ajoutez silencieusement une nouvelle ligne de fin au moment où elle écrit un fichier qui lui manquait (sauf si le fichier n'est vide).

  • ce n'est que si au moment de l'écriture! (C'est-à-dire que vous pouvez utiliser: W, vous pouvez utiliser: E Pour vérifier que le fichier est toujours comme vous l'avez ouvert ... (c'est-à-dire: il affiche toujours le "nom de fichier" [la dernière ligne n'est pas terminé] N Line, M PLACES MAR). Lorsque vous économisez, une nouvelle ligne est ajoutée silencieusement, sans un avertissement spécifique (il dit combien d'octets cela sauve, mais cela est dans la plupart des cas suffisamment pour connaître une nouvelle ligne a été ajouté) (grâce à @Jiliagre pour me parler de la Ouverture du message VI, cela m'a aidé à trouver un moyen de savoir quand le changement se produit vraiment)

  • Ceci (correction silencieuse) est [~ # ~] posix [~ # ~ # ~] Comportement! (Voir @ Barefoot-io Réponse pour les références)

36
Olivier Dulac

C'est le comportement attendu vi.

Votre fichier a une dernière ligne incomplète si strictement parlant (c'est-à-dire en fonction de la norme POSIX), ce n'est pas un fichier texte, mais un fichier binaire.

vi qui est un éditeur de fichiers texte, pas un binaire, le répare gracieusement lorsque vous l'enregistrez.

Cela permet aux autres outils de fichier texte tels que wc, sed et les goûts de fournir la sortie attendue. Notez que vi n'est pas silencieux sur le problème:


$ 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

Remarque, pour obtenir des indices sur la version vi que vous utilisez, vous pouvez utiliser :ve commande. Il montre ici que j'utilise un héritage SVR4 un ici, certainement pas vim:

:ve
Version SVR4.0, Solaris 2.5.0

Apparemment, le vôtre indique:

:ve
Version 3.10

Cela signifie probablement AIX vi est basé sur le code source SVR3.

En tout cas, ce comportement et le [Incomplete last line] Le message d'avertissement a été dans le code source de la joie hérité de Bill Joy vi Code source depuis au moins 1979 et AFAIK, conservé dans toutes les branches créées à partir des versions de code source System V, à partir de laquelle des UNIX propriétaires comme AIX ont été construits.

Chronologiquement parlant, ce comportement n'est alors pas une conséquence de la conformité de POSIX, mais une conséquence de la décision initiale de Bill Joy d'être utile avec les utilisateurs modifiant des fichiers texte faux, puis une décennie plus tard, la décision du Comité de la POSIX de conserver cette tolérance.

Si vous utilisez ed au lieu de vi, vous remarquerez que le premier est plus verbeux sur la question, au moins si votre ed provient de SVR3 ou de la nouvelle branche Source:

$ ed file
'\n' appended
8
q

Notez également qu'un fichier vide est un fichier texte valide qui contient des lignes zéro. Comme il n'y a alors pas de ligne non déterminée pour corriger, vi _ n'apporte pas une nouvelle ligne lors de la sauvegarde du fichier.

28
jlliagre

POSIX nécessite ce comportement, de sorte que ce n'est en aucune manière inhabituelle.

À partir du Manuel POSIX VI :

Fichiers d'entrée

Voir la section Fichiers d'entrée de la commande ex pour une description des fichiers d'entrée pris en charge par la commande VI.

Suivre le sentier au POSIX EX Manuel :

Fichiers d'entrée

Les fichiers d'entrée doivent être des fichiers texte ou des fichiers qui seraient des fichiers texte sauf une dernière ligne incomplète qui n'est plus que {line_max} -1 octets de longueur et ne contient aucun caractères nul. Par défaut, toute dernière ligne incomplète doit être traitée comme si elle avait une trailing <nouvelle ligne>. L'édition d'autres formes de fichiers peut éventuellement être autorisée par des exemples d'exécution.

La section Fichiers de sortie du manuel VI redirige également à EX:

Fichiers de sortie

La sortie d'EX doit être des fichiers texte.

Une paire de définitions de POSIX:

.397 Fichier texte

Un fichier contenant des caractères organisés en zéro ou plus de lignes. Les lignes ne contiennent pas de caractères nul et aucune ne peut dépasser {line_max} octets de longueur, y compris le caractère <nouveau>. Bien que POSIX.1-2008 ne distingue pas les fichiers texte et les fichiers binaires (voir la norme ISO C), de nombreux utilitaires ne produisent que des sorties prévisibles ou significatives lors de la fonctionnement des fichiers texte. Les utilitaires standard qui ont de telles restrictions spécifient toujours des "fichiers texte" dans leurs sections STDIN ou Fichiers d'entrée.

ligne 3.206

Une séquence de caractères zéro ou plus non <wline> plus un caractère de terminaison <nouveau>.

Ces définitions dans le contexte de ces extraits de la page manuelle signifient que, tandis qu'une implémentation EX/VI conforme doit accepter un fichier texte mal formé si la seule déformation de ce fichier est une nouvelle ligne finale absente, lors de la rédaction de la mémoire tampon de ce fichier, le résultat doit être un fichier texte valide.

Bien que ce message a fait référence à l'édition 2013 de la norme POSIX, les stipulations pertinentes apparaissent également dans l'édition beaucoup plus ancienne de 1997 .

Enfin, si vous trouvez la nouvelle lancement de l'EX NOUVELLINE, vous vous sentirez profondément violé par la septième Edition Unix (1979) Intolérante ED. à partir du manuel :

Lors de la lecture d'un fichier, ed rejeter ASCII caractères Nul et tous les caractères après la dernière nouvelle ligne. Il refuse de lire des fichiers contenant des caractères non ASCII.

51
Barefoot IO

Le texte qui manque de manière incorrecte de la nouvelle ligne ultime via une coque while boucle entraîne la dernière ligne étant mis au rebut silencieusement.

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

S'assurer qu'il y ait une nouvelle ligne ultime est la droite et la sain d'esprit et la valeur correcte. L'autre option consiste à connaître et à avoir le temps d'audit tout code shell qui touche du texte sans ultime nouvelle ligne ou de perdre la dernière ligne du texte.

1
thrig

Je ne me souviens pas d'un autre comportement qu'un nouveau-ligne est ajouté à la fin d'un fichier (en utilisant vi depuis le milieu des années 80).

Les ~ Indique qu'une ligne à l'écran qui ne fait pas partie du texte, et non que le fichier ne se termine pas dans une nouvelle ligne. (Vous pouvez être difficile à tracer des erreurs si vous mettez un ~ sur la dernière ligne de scripts shell). Si vous chargez un fichier court avec une nouvelle ligne à la fin, vous verrez le ~ vous-même et réfuter que votre pensée indique un texte de fin de nouvelle ligne.

1
Anthon