![¿Vi agrega silenciosamente una nueva línea (LF) al final del archivo?](https://rvso.com/image/83704/%C2%BFVi%20agrega%20silenciosamente%20una%20nueva%20l%C3%ADnea%20(LF)%20al%20final%20del%20archivo%3F.png)
Tengo problemas para entender un comportamiento extraño: vi parece agregar una nueva línea (ASCII: LF, ya que es Unix (AIX) sistema) al final del archivo, cuando NO lo escribí específicamente.
Edito el archivo como tal en vi (teniendo cuidado de no ingresar una nueva línea al final):
# 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.
Espero que vi lo guarde "tal cual", para tener 39 bytes: 10 caracteres ASCII en cada una de las primeras tres líneas (números del 1 al 9, seguidos de una nueva línea (LF en mi sistema)) y solo 9 en la última línea (caracteres 1 a 9, sin nueva línea final/LF).
Pero aparece cuando lo guardo que es40bytes (en lugar de 39), yod muestra un LF terminal:
# 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 creo el archivo con printf haciendo exactamente lo que hice dentro de vi, funciona como se esperaba:
# ## 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.
Ambos archivos (foo (40 caracteres) y foo2 (39 caracteres) aparecen exactamente iguales si los vuelvo a abrir con vi...
Y si abro foo2 (39 caracteres, sin nueva línea final) en vi ysimplemente hazlo :wq
sin editarlo en absoluto, dice que escribe 40 caracteres y aparece el salto de línea.
No puedo tener acceso a un vi más reciente (hago esto en AIX, vi (noEmpuje) versión 3.10 creo? (sin "-versión" u otros medios para saberlo)).
# strings /usr/bin/vi | grep -i 'version.*[0-9]'
@(#) Version 3.10
¿Es normal que vi (y quizás no en una versión más reciente? ¿O Vim?) agregue silenciosamente una nueva línea al final de un archivo? (Pensé que ~ indicaba que la línea anterior NO terminaba con una nueva línea).
--
Editar:algunas actualizaciones adicionales y un pequeño resumen, con un gran agradecimiento a las respuestas a continuación:
vi agrega silenciosamente una nueva línea al final en el momento en que escribe un archivo que carece de ella (a menos que el archivo esté vacío).
¡solo lo hace en el momento de escribir! (es decir, hasta que :w, puede usar :e para verificar que el archivo sigue tal como lo abrió... (es decir: todavía muestra "nombre de archivo" [La última línea no está completa] N línea, carácter M). Cuando guarda, se agrega una nueva línea silenciosamente, sin una advertencia específica (sí dice cuántos bytes guarda, pero en la mayoría de los casos esto no es suficiente para saber que se agregó una nueva línea) (gracias a @jiliagre por hablarme sobre el Al abrir el mensaje vi, me ayudó a encontrar una manera de saber cuándo ocurre realmente el cambio)
Esta (corrección silenciosa) esPOSIX¡comportamiento! (consulte la respuesta de @barefoot-io para obtener referencias)
Respuesta1
POSIX requiere este comportamiento, por lo que no es inusual.
Desde elmanual de POSIX vi:
ARCHIVOS DE ENTRADA
Consulte la sección ARCHIVOS DE ENTRADA del comando ex para obtener una descripción de los archivos de entrada admitidos por el comando vi.
Siguiendo el rastro hacia elmanual POSIX ex:
ARCHIVOS DE ENTRADA
Los archivos de entrada serán archivos de texto o archivos que serían archivos de texto, excepto por una última línea incompleta que no tenga más de {LINE_MAX}-1 bytes de longitud y no contenga caracteres NUL. De forma predeterminada, cualquier última línea incompleta se tratará como si tuviera una <nueva línea> al final. Opcionalmente, las implementaciones ex pueden permitir la edición de otras formas de archivos.
La sección ARCHIVOS DE SALIDA del manual vi también redirige a ex:
ARCHIVOS DE SALIDA
La salida de ex serán archivos de texto.
Un par de definiciones POSIX:
Un archivo que contiene caracteres organizados en cero o más líneas. Las líneas no contienen caracteres NUL y ninguna puede exceder {LINE_MAX} bytes de longitud, incluido el carácter <nueva línea>. Aunque POSIX.1-2008 no distingue entre archivos de texto y archivos binarios (consulte el estándar ISO C), muchas utilidades solo producen resultados predecibles o significativos cuando operan con archivos de texto. Las utilidades estándar que tienen tales restricciones siempre especifican "archivos de texto" en sus secciones STDIN o INPUT FILES.
Una secuencia de cero o más caracteres que no son <nueva línea> más un carácter <nueva línea> de terminación.
Estas definiciones en el contexto de estos extractos de páginas del manual significan que, si bien una implementación ex/vi conforme debe aceptar un archivo de texto con formato incorrecto si la única deformidad de ese archivo es una nueva línea final ausente, al escribir el búfer de ese archivo, el resultado debe ser un archivo de texto válido.
Si bien esta publicación hace referencia a la edición 2013 del estándar POSIX,Las estipulaciones pertinentes también aparecen en la edición de 1997, mucho más antigua..
Por último, si no le agrada la apensión de nueva línea de su ex, se sentirá profundamente violado por la intolerante edición de la Séptima Edición de UNIX (1979).Del manual:
Al leer un archivo, ed descarta los caracteres ASCII NUL y todos los caracteres después de la última nueva línea. Se niega a leer archivos que contengan caracteres que no sean ASCII.
Respuesta2
Este es el vi
comportamiento esperado.
Su archivo tiene una última línea incompleta, por lo que estrictamente hablando (es decir, según el estándar POSIX), no es un archivo de texto sino un archivo binario.
vi
que es un editor de archivos de texto, no binario, lo arregla elegantemente cuando lo guardas.
Esto permite que otras herramientas de archivos de texto como wc
y sed
similares proporcionen el resultado esperado. Tenga en cuenta que vi
no guarda silencio sobre el tema:
$ 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
Tenga en cuenta que para obtener algunas pistas sobre qué vi
versión está ejecutando, puede usar el :ve
comando. Aquí se muestra que estoy usando un SVR4 heredado, definitivamente no vim
:
:ve
Version SVR4.0, Solaris 2.5.0
Aparentemente el tuyo dice:
:ve
Version 3.10
Eso probablemente significa que AIX vi
se basa en el código fuente SVR3.
En cualquier caso, este comportamiento y el [Incomplete last line]
mensaje de advertencia han estado en el vi
código fuente heredado de Bill Joy desde al menos 1979 y AFAIK, conservados en todas las ramas creadas a partir de las versiones del código fuente de System V, a partir de las cuales se construyeron Unix propietarios como AIX.
Cronológicamente hablando, este comportamiento no es una consecuencia de la conformidad con POSIX, sino más bien una consecuencia de la decisión original de Bill Joy de ayudar a los usuarios a editar archivos de texto falsos y luego, una década después, la decisión del comité POSIX de mantener esta tolerancia.
Si usa ed
en lugar de vi
, notará que el primero es más detallado sobre el problema, al menos si es ed
de SVR3 o de una rama fuente más reciente:
$ ed file
'\n' appended
8
q
Tenga en cuenta también que un archivo vacío es un archivo de texto válido que no contiene líneas. Como no hay ninguna línea sin terminar que corregir, vi
no agrega una nueva línea al guardar el archivo.
Respuesta3
El texto que incorrectamente carece de la última nueva línea ejecutada a través de un while
bucle de shell da como resultado que la última línea se descarte silenciosamente.
$ (echo transaction 1; echo -n transaction 2) \
| while read line; do echo $line; done
transaction 1
$
Garantizar que haya una nueva línea definitiva es el valor predeterminado correcto, sensato y adecuado. La otra opción implica saber y tener tiempo para auditar todo el código shell que toca texto al que le falta la nueva línea final, o correr el riesgo de perder la última línea del texto.
Respuesta4
No recuerdo ningún otro comportamiento en el que se agregue una nueva línea al final de un archivo (usándose vi
desde mediados de los 80).
Indica ~
que hay una línea en la pantalla que no es parte del texto, no que el archivo no termina en una nueva línea. (Puede resultar difícil rastrear errores si coloca un ~
en la última línea de los scripts de Shell). Si carga un archivo corto con una nueva línea al final, lo verá ~
usted mismo y refutará que su pensamiento indica texto sin fin de nueva línea.