Vi ajoute-t-il silencieusement une nouvelle ligne (LF) à la fin du fichier?

36

J'ai du mal à comprendre un comportement étrange: vi semble ajouter une nouvelle ligne (ASCII: LF, car il s’agit d’un système Unix ( AIX )) à la fin du fichier, alors que je ne l’avais PAS spécifiquement écrit.

J'édite le fichier tel quel dans vi (en prenant soin de ne pas saisir de 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.

J'espère que vi le sauvegardera "tel quel", donc 39 octets: 10 caractères ASCII sur chacune des trois premières lignes (numéros 1 à 9, suivis d'un saut de ligne (LF sur mon système)) et seulement 9 sur la dernière line (caractères 1 à 9, pas de nouvelle ligne / LF).

Mais il semble que lorsque je le sauvegarde il est de 40 octets (au lieu de 39), et od montre un LF final :

# 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 printf faisant exactement ce que j'ai fait dans vi, cela fonctionnera 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 de la même manière si je les rouvre avec vi ...

Et si j'ouvre foo2 (39 caractères, sans nouvelle ligne) dans vi et que je me contente de le :wqmodifier , il dit qu'il écrit 40 caractères et que le saut de ligne apparaît!

Je ne peux pas avoir accès à un vi plus récent (je le fais sous 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-il normal que vi (et peut-être pas dans une version plus récente? Ou Vim?) Ajoute silencieusement une nouvelle ligne à la fin d'un fichier? (Je pensais que le ~ indiquait que la ligne précédente ne finissait pas par une nouvelle ligne.)

-

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

  • vi ajoute silencieusement une nouvelle ligne au moment où il écrit un fichier qui en manque (sauf si le fichier est vide).

  • il ne le fait qu'au moment de l'écriture! (c.-à-d. jusqu'à ce que: w, vous puissiez utiliser: e pour vérifier que le fichier est toujours tel que vous l'avez ouvert ... (c.-à-d. qu'il affiche toujours "nom du fichier" [la dernière ligne n'est pas complète] N ligne, caractère M). Lorsque vous sauvegardez, une nouvelle ligne est ajoutée silencieusement, sans avertissement spécifique (le nombre d'octets sauvegardés est indiqué, mais dans la plupart des cas, cela ne suffit pas de savoir qu'une nouvelle ligne a été ajoutée) (merci à @jiliagre de m'avoir parlé de la En ouvrant le message vi, cela m’a aidé à trouver un moyen de savoir quand le changement se produit réellement)

  • Ceci (correction silencieuse) est le comportement POSIX ! (voir la réponse @ barefoot-io pour les références)

Olivier Dulac
la source
Juste pour être complet, quelle version d'AIX (version complète).
EightBitTony
2
Je ne suis pas au courant que vi ait cette option pour AIX - apparaît uniquement avec vim
Jeff Schaller
1
@JeffSchaller: merci pour le lien. Malheureusement, vi n'a pas ": set noeol" ni même l'option -b pour s'ouvrir en mode binaire ...
Olivier Dulac
1
Vous pourrez peut-être obtenir la viversion ou au moins un indice sur son origine en exécutant la :vecommande.
mardi
1
@ThomasDickey en effet. Pour une raison quelconque, IBM a supprimé la expage de manuel où la :vercommande est normalement documentée.
jeudi

Réponses:

28

C'est le vicomportement attendu .

Votre fichier comporte une dernière ligne incomplète, donc à proprement parler (c’est-à-dire selon la norme POSIX), il ne s’agit pas d’un fichier texte, mais d’un fichier binaire.

vi qui est un éditeur de fichier texte, pas un binaire, le corrige gracieusement lorsque vous enregistrez.

Cela permet à d’autres outils de fichiers texte tels que wc , sedet , de fournir le résultat attendu. Notez que ce vin'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 ce que vi version que vous utilisez, vous pouvez utiliser la :vecommande. Cela montre ici que j'utilise un SVR4 hérité ici, certainement pas vim:

:ve
Version SVR4.0, Solaris 2.5.0

Apparemment, le vôtre dit:

:ve
Version 3.10

Cela signifie probablement que AIX viest basé sur le code source SVR3.

En tout état de cause, ce comportement et le [Incomplete last line]message d'avertissement vifigurent dans le code source de Bill Joy hérité depuis 1979 au moins et AFAIK, conservés dans toutes les branches créées à partir de versions de code source de System V, à partir desquelles des systèmes Unix propriétaires, tels qu'AIX, ont été créés.

Chronologiquement, ce comportement n’est donc pas une conséquence de la conformité à POSIX, mais bien une conséquence de la décision initiale de Bill Joy d’aider les utilisateurs à modifier des fichiers texte fictifs, puis, dix ans plus tard, de la décision du comité POSIX de conserver cette tolérance.

Si vous utilisez à la edplace de vi, vous remarquerez que la première est plus détaillée sur le problème, du moins si vous edvenez de SVR3 ou d'une branche source plus récente:

$ ed file
'\n' appended
8
q

Notez également qu'un fichier vide est un fichier texte valide qui ne contient aucune ligne. Comme il n’existe alors aucune ligne non terminée à corriger, vin’ajoutez pas de nouvelle ligne lors de l’enregistrement du fichier.

jlliagre
la source
1
Je crois que vous confondez vim avec vi;) l'héritage vi est beaucoup moins verbeux que cela ...
Olivier Dulac
@OlivierDulac Je ne les confonds pas. Ce test a été effectué à l'aide de l'héritage SVR4, viexactement comme le fait l'OP, bien que sous Unix différent. Ce n'est pas vimou un autre clone. Réponse mise à jour pour clarifier cela.
jeudi
@OlivierDulac Hmm, je viens de remarquer que vous êtes en fait l'OP. Il semble qu'AIX utilise une branche plus ancienne de System V pour sa vimise en œuvre. Peut-être SVR3. Êtes-vous sûr qu'il n'y a pas de [Incomplete last line]message lorsque vous ouvrez le fichier?
jeudi
@OlivierDulac Ce lien semble impliquer que ce même message peut être affiché par l' viimplémentation AIX : www-01.ibm.com/support/docview.wss?uid=isg1IZ27694
jlliagre
Je vais essayer de voir ça demain
Olivier Dulac
51

POSIX requiert ce comportement, ce qui n’a donc rien d’inhabituel.

Du manuel POSIX vi :

FICHIERS D'ENTRÉE

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

Suivre la piste jusqu'au manuel ex POSIX :

FICHIERS D'ENTRÉE

Les fichiers d'entrée doivent être des fichiers texte ou des fichiers qui seraient des fichiers texte, à l'exception d'une dernière ligne incomplète ne dépassant pas {LINE_MAX} -1 octets et ne contenant pas de caractères NUL. Par défaut, toute dernière ligne incomplète doit être traitée comme si elle comportait une <nouvelle ligne> de fin. L’édition d’autres formes de fichiers peut éventuellement être autorisée par des implémentations antérieures.

La section OUTPUT FILES du manuel vi redirige également vers ex:

FICHIERS DE SORTIE

Le résultat de ex doit être un fichier texte.

Une paire de définitions POSIX:

3.397 Fichier texte

Un fichier qui contient des caractères organisés en zéro ligne ou plus. Les lignes ne contiennent pas de caractères NUL et aucune ne peut dépasser {LINE_MAX} octets, y compris le caractère <nouvelle ligne>. Bien que POSIX.1-2008 ne fasse pas la distinction entre les fichiers texte et les fichiers binaires (voir la norme ISO C), de nombreux utilitaires ne produisent que des résultats prévisibles ou significatifs s’ils fonctionnent avec des fichiers texte. Les utilitaires standard qui ont de telles restrictions spécifient toujours des "fichiers texte" dans leurs sections STDIN ou INPUT FILES.

Ligne 3.206

Une séquence de zéro ou plusieurs caractères non <nouvelle ligne> plus un caractère <nouvelle ligne> de fin.

Ces définitions dans le contexte de ces extraits de page de manuel signifient que, même si une implémentation conforme ex / vi doit accepter un fichier texte mal formé si la seule déformation de ce fichier est une nouvelle ligne finale absente, le résultat doit être un fichier texte valide lors de l'écriture de la mémoire tampon de ce fichier.

Bien que cet article fasse référence à l'édition 2013 du standard POSIX, les stipulations pertinentes apparaissent également dans l'édition beaucoup plus ancienne de 1997 .

Enfin, si vous trouvez que la nouvelle version de l'appex est indésirable, vous vous sentirez profondément violée par l'intolérance éd. De Seventh Edition UNIX (1979). Du manuel :

Lors de la lecture d'un fichier, ed ignore les caractères ASCII NUL et tous les caractères suivant la dernière ligne. Il refuse de lire les fichiers contenant des caractères non-ASCII.

Pieds nus IO
la source
merci, cela répond à ma question. Je vais juste attendre quelques jours de plus au cas où une meilleure réponse se gâterait, mais pour le moment, je pense que vous pouvez être la réponse acceptée.
Olivier Dulac
Très bien fait sur la réponse parfaitement documentée, directement à partir des spécifications! :)
Wildcard
1
@Wildcard, le comportement a toutefois précédé les spécifications.
jeudi
@ jlliagre, à moins que vous n'ayez un mémoire de Bill Joy ou du créateur de ex(je ne connais pas son nom), je pense que les spécifications POSIX sont aussi bonnes que possible. ;) Plus proche de "source originale" à ce stade, même s'il est vrai qu'ils ont commencé comme plus ou moins des descriptions de fonctionnalités existantes.
Wildcard
3
@Wildcard a exété co-écrit par Bill Joy et Chuck Alley ( web.cecs.pdx.edu/~kirkenda/joy84.html .) Je ne remets pas en question les spécifications POSIX et le fait que les viversions actuelles le suivent, je ne fais que signaler le comportement longtemps le précède.
jeudi
1

Je ne me souviens d'aucun autre comportement qu'une nouvelle ligne est ajoutée à la fin d'un fichier (utilisation videpuis le milieu des années 80).

Le ~signe qu'une ligne à l'écran qui ne fait pas partie du texte ne signifie pas que le fichier ne se termine pas par une nouvelle ligne. (Il peut être difficile de détecter les erreurs si vous mettez un ~dernier mot de shell dans la dernière ligne). Si vous chargez un fichier court avec une nouvelle ligne à la fin, vous verrez apparaître ~vous - même et réfutez le fait que votre pensée indique un texte ne se terminant pas par une nouvelle ligne.

Anthon
la source
ce qui me surprend, c’est l’ajout d’une nouvelle ligne ... Je pense que vi ne l’ajouterai pas en silence, mais il semblerait que ce soit le cas ... Je cherche une explication à cette attitude (le fait troublant est que j’ouvre foo2 (sans derrière LF) et juste: wq, ça change son contenu ... donc ça me montre quelque chose mais enregistre une autre chose ... bizarre, c'est le moins qu'on puisse dire ^^
Olivier Dulac
dans son prédécesseur ( ed), vous créez des lignes et les modifiez, pas en ajoutant des caractères. J'ai toujours pensé à vi comme éditeur en ligne. Mais je comprends ta surprise.
Anthon
1

Le texte qui manque de manière incorrecte le saut de ligne ultime dans une whileboucle de coque entraîne la suppression silencieuse de la dernière ligne.

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

S'assurer qu'il y a une nouvelle ligne ultime est le bon choix, sain et sensé. L'autre option consiste à connaître et à disposer du temps nécessaire pour auditer tout code shell touchant un texte ne disposant pas de saut de ligne ultime, ou risquer de perdre la dernière ligne du texte.

thrig
la source