¿Qué sentido tiene agregar una nueva línea al final de un archivo?

166

Algunos compiladores (especialmente los de C o C ++) le dan advertencias sobre:

No new line at end of file

Pensé que esto sería un problema exclusivo de los programadores C, pero github muestra un mensaje en la vista de confirmación:

\ No newline at end of file

para un archivo PHP.

Entiendo lo del preprocesador explicado en este hilo , pero ¿qué tiene esto que ver con PHP? ¿Es lo mismo include()o está relacionado con el tema \r\nvs \n?

¿Cuál es el punto de tener una nueva línea al final de un archivo?

Philipp Stephan
fuente
Duplicar de SO: stackoverflow.com/questions/729692/…
AlikElzin-kilaka
2
Hacer enojar a la gente.
Andrew
44
Si tiene catel archivo, el siguiente mensaje se agregará a la "línea" final si no termina con una nueva línea.
Aaron Franke

Respuestas:

188

No se trata de agregar una nueva línea adicional al final de un archivo, se trata de no eliminar la nueva línea que debería estar allí.

Un archivo de texto , bajo unix, consta de una serie de líneas , cada una de las cuales termina con un carácter de nueva línea ( \n). Por lo tanto, un archivo que no está vacío y no termina con una nueva línea no es un archivo de texto.

Las utilidades que se supone que operan en archivos de texto pueden no funcionar bien con archivos que no terminan con una nueva línea; Las utilidades históricas de Unix pueden ignorar el texto después de la última línea nueva, por ejemplo. Las utilidades de GNU tienen una política de comportarse decentemente con archivos que no son de texto, al igual que la mayoría de las otras utilidades modernas, pero aún puede encontrar comportamientos extraños con los archivos a los que les falta una nueva línea final¹.

Con GNU diff, si uno de los archivos que se comparan termina con una nueva línea pero no con el otro, es importante tener en cuenta ese hecho. Dado que diff está orientado a líneas, no puede indicar esto almacenando una nueva línea para uno de los archivos, pero no para los demás; las nuevas líneas son necesarias para indicar dónde comienza y termina cada línea en el archivo diff . Así que diff usa este texto especial \ No newline at end of filepara diferenciar un archivo que no terminó en una nueva línea de un archivo que sí lo hizo.

Por cierto, en un contexto C, un archivo fuente de manera similar consiste en una serie de líneas. Más precisamente, una unidad de traducción se ve en una implementación definida como una serie de líneas, cada una de las cuales debe terminar con un carácter de nueva línea ( n1256 §5.1.1.1). En sistemas unix, el mapeo es sencillo. En DOS y Windows, cada secuencia CR LF ( \r\n) se asigna a una nueva línea ( \n; esto es lo que siempre sucede cuando se lee un archivo abierto como texto en estos sistemas operativos). Existen algunos sistemas operativos que no tienen un carácter de nueva línea, sino que tienen registros de tamaño fijo o variable; En estos sistemas, la asignación de archivos a la fuente C introduce un\nal final de cada registro. Si bien esto no es directamente relevante para Unix, significa que si copia un archivo fuente C que falta su nueva línea final en un sistema con archivos de texto basados ​​en registros, luego cópielo de nuevo, o terminará con el archivo incompleto última línea truncada en la conversión inicial, o una nueva línea adicional añadida durante la conversión inversa.

¹ Ejemplo: la salida del ordenamiento GNU siempre termina con una nueva línea. Entonces, si al archivo foole falta su nueva línea final, encontrará que sort foo | wc -cinforma un carácter más que cat foo | wc -c.

Gilles
fuente
Con respecto a "... series de líneas, cada una de las cuales debe terminar con un carácter de nueva línea (n1256 §5.1.1.1)" -> Al volver a ver el C11dr N1570 más reciente, no encontré soporte para eso más que tal vez: "Un archivo fuente que no está vacío terminará en un carácter de nueva línea, que no estará precedido inmediatamente por un carácter de barra diagonal inversa antes de que se produzca dicho empalme". §5.1.1.2 2, pero eso parece estar restringido a las especificaciones de empalme.
chux
@chux Esa oración también está presente en n1256. La última línea debe terminar con un carácter de nueva línea. Las líneas que no son la última línea obviamente también deben terminar con un carácter de nueva línea para indicar que esa línea termina y comienza la siguiente. Por lo tanto, cada línea debe terminar con un carácter de nueva línea.
Gilles
Hmmm, para mí, esa línea "" Un archivo fuente ... tiene lugar el empalme "podría limitarse a cómo las consideraciones de empalme y no los archivos en general. Sin embargo, veo cómo se puede ver lo contrario. Tal vez buscaré una publicación que se centra en eso.
Chux
> "Entonces diff usa este texto especial \ No hay nueva línea al final del archivo para diferenciar un archivo que no terminó en una nueva línea de un archivo que sí lo hizo". Git muestra este texto no solo cuando compara archivos. Pero incluso cuando se agrega un nuevo archivo a git. Entonces este argumento no es válido, supongo.
Viktor Kruglikov
> "Las utilidades que se supone que operan en archivos de texto pueden no funcionar bien con archivos que no terminan con una nueva línea" No creo que sea asunto de git preocuparse por problemas de tan bajo nivel como faltar \ n debido a POSIX requisitos Creo que si git muestra este mensaje, la razón debería estar en los problemas de control de la fuente .
Viktor Kruglikov
42

No necesariamente la razón, sino una consecuencia práctica de los archivos que no terminan con una nueva línea:

Considere lo que sucedería si quisiera procesar varios archivos usando cat. Por ejemplo, si desea encontrar la palabra fooal comienzo de la línea en 3 archivos:

cat file1 file2 file3 | grep -e '^foo'

Si la primera línea en el archivo 3 comienza con foo, pero el archivo 2 no tiene una final \ndespués de su última línea, grep no encontrará esta ocurrencia, porque grep verá la última línea en el archivo 2 y la primera línea en el archivo 3 como un solo línea.

Entonces, por consistencia y para evitar sorpresas, trato de mantener mis archivos siempre terminando con una nueva línea.

Sergio Acosta
fuente
¿Pero es asunto de git preocuparse por la concatenación de archivos?
Viktor Kruglikov
¿No es lógico que debas poner '\n'en funcionamiento el gato ...
Andrew
3
Es como decir: "A veces agrego cadenas que tienen \nespacios en blanco en los extremos, por lo que para mantener las cosas coherentes, siempre pongo \n _____ambos extremos de mis cadenas". Bueno, no, lo correcto es tener sus cadenas recortadas y luego concatenarlas adecuadamente.
Andrew
16

Hay dos aspectos:

  1. Hay / hubo algunos compiladores de C que no pueden analizar la última línea si no termina con una nueva línea. El estándar C especifica que un archivo C debe terminar con una nueva línea (C11, 5.1.1.2, 2.) y que una última línea sin una nueva línea produce un comportamiento indefinido (C11, J.2, segundo elemento). Quizás por razones históricas, porque algún proveedor de tal compilador era parte del comité cuando se redactó el primer estándar. De ahí la advertencia de GCC.

  2. diffLos programas (como los utilizados por git diffGithub, etc.) muestran diferencias línea por línea entre los archivos. Por lo general, imprimen un mensaje cuando solo un archivo termina con una nueva línea porque de lo contrario no vería esta diferencia. Por ejemplo, si la única diferencia entre dos archivos es la presencia del último carácter de nueva línea, sin la sugerencia, parecería que ambos archivos son iguales, cuando diffy cmpdevuelve un código de salida de éxito desigual y las sumas de comprobación de los archivos (por ejemplo, a través de md5sum) no coinciden.

maxschlepzig
fuente
tiene sentido con el programa diff
Thamaraiselvam
Parece que las diferencias deberían ser más inteligentes.
Andrew
@ Andrew, no, no lo hace. diffse espera que imprima las diferencias si hay alguna. Y si un archivo tiene una nueva línea como último carácter mientras que el otro no, entonces esa diferencia debe ser notable de alguna manera en la salida.
maxschlepzig
Su última afirmación es correcta. Sin embargo, el visor de diferencias no tiene que mostrar "nuevas líneas" ( \n) para empezar, sino que simplemente puede mostrar "nuevas líneas".
Andrew
10

Lo \ No newline at end of fileque obtiene de github aparece al final de un parche (en diffformato , vea la nota al final de la sección "Formato unificado").

A los compiladores no les importa si hay una nueva línea o no al final de un archivo, pero git(y las diff/ patchutilidades) deben tenerlas en cuenta. Hay muchas razones para eso. Por ejemplo, olvidar agregar o quitar una nueva línea al final de un archivo cambiaría su hashsum ( md5sum/ sha1sum). Además, los archivos no siempre son programas, y un final \npuede hacer alguna diferencia.

Nota : sobre la advertencia de los compiladores de C, supongo que insisten en una nueva línea final para fines de compatibilidad con versiones anteriores. Los compiladores muy antiguos podrían no aceptar la última línea si no termina con \n(u otra secuencia de caracteres de fin de línea dependiente del sistema).

Stéphane Gimenez
fuente
77
"Supongo que insisten en una nueva línea final para fines de compatibilidad con versiones anteriores" - No, insisten en ello porque el estándar C lo exige .
MestreLion
1
@MestreLion C requiere una nueva línea final para el código fuente C (C11 §5.1.1.2 2). Tenga en cuenta que para E / S del archivo de texto , C tiene "Si la última línea requiere un carácter de nueva línea de terminación está definida por la implementación". §7.21.2 2
chux
¿Quién está usando compiladores muy antiguos? Deja de usarlos.
Andrew
1
@MestreLion: ¿Y por qué crees que el estándar C lo exige ...
Stéphane Gimenez
@ StéphaneGimenez: consistencia, mejor compatibilidad e interoperabilidad entre diferentes sistemas operativos (POSIX también define líneas que terminan en '\ n')
MestreLion
4

También existe el punto de mantener la historia diferencial. Si un archivo finaliza sin un carácter de nueva línea, las utilidades diff verán que agregar cualquier cosa al final del archivo cambiará esa última línea (porque \nse le está agregando).

Esto podría causar resultados no deseados con comandos como git blamey hg annotate.

Hosam Aly
fuente
Parece que los diferenciales solo necesitan ser más inteligentes.
Andrew
1
Las diferentes herramientas son ser inteligentes. Notan el cambio sutil en el archivo (lo cual es importante porque inevitablemente cambiará el hash del archivo). Y tanto GNU diff como git diff aceptan una -wopción para ignorar los cambios en los espacios en blanco al generar datos para humanos.
joeytwiddle
4

POSIX, este es un conjunto de estándares especificados por IEEE para mantener la compatibilidad entre sistemas operativos.

Una de ellas es la definición de una "línea" que es una secuencia de cero o más no caracteres más un carácter de nueva línea de terminación.

Por lo tanto, para que esa última línea se reconozca como una "línea" real, debe tener un nuevo carácter de línea de terminación.

Esto es importante si depende de las herramientas del sistema operativo para decir el recuento de líneas o dividir / ayudar a analizar su archivo. Dado que PHP es un lenguaje de script, es completamente posible, especialmente en sus primeros días o incluso ahora (no tengo idea / postulación) tenía dependencias del sistema operativo como ese.

En realidad, la mayoría de los sistemas operativos no son totalmente compatibles con POSIX y a los humanos no les gusta esa máquina o ni siquiera se preocupan por terminar nuevas líneas. Entonces, para la mayoría de las cosas, es una mezcla heterogénea de todo, ya sea que se preocupe por eso, advierta o simplemente que ir al último fragmento de texto sea realmente una línea, así que simplemente inclúyalo.

usuario3379747
fuente