To clarify, the LF (aka \n
or newline) character is the line delimiter, it's not the line separator. A line is not finished unless it's terminated by a newline character. A file that only contains a\nb
is not a valid text file because it contains characters after the last line. Same for a file that contains only a
. A file that contains a\n
contains one non-empty line.
So a file that ends with at least one empty line ends with two newline characters or contains a single newline character.
If:
tail -c 2 file | od -An -vtc
Outputs \n
or \n \n
, then the file contains at least one trailing empty line. If it outputs nothing, then that's an empty file, if it outputs <anything-but-\0> \n
, then it ends in a non-empty line. Anything else, it's not a text file.
Now, to use that to find files that end in an empty line, OK that's efficient (especially for large files) in that it only reads the last two bytes of the files, but first the output is not easily parsable programmatically especially considering that it's not consistent from one implementation of od
to the next, and we'd need to run one tail
and one od
per file.
find . -type f -size +0 -exec gawk '
ENDFILE{if ($0 == "") print FILENAME}' {} +
(to find files ending in an empty line) would run as few commands as possible but would mean reading the full content of all files.
Ideally, you'd need a shell that can read the end of a file by itself.
With zsh
:
zmodload zsh/system
for f (**/*(D.L+0)) {
{
sysseek -w end -2
sysread
[[ $REPLY = $'\n' || $REPLY = $'\n\n' ]] && print -r -- $f
} < $f
}
I am running Ubuntu 18.04 and /usr/lib/systemd did not contain any services for http or apache2. However, I executed the following command:
sudo find / -mount -type f -exec grep -e "PrivateTmp" '{}' ';' -print
and found in /lib/systemd/apache2.service the PrivateTmp=true. Changing true to false and executing
systemctl daemon-restart
systemctl restart apache2
fixed the problem.
Best Answer
It's not about adding an extra newline at the end of a file, it's about not removing the newline that should be there.
A text file, under unix, consists of a series of lines, each of which ends with a newline character (
\n
). A file that is not empty and does not end with a newline is therefore not a text file.Utilities that are supposed to operate on text files may not cope well with files that don't end with a newline; historical Unix utilities might ignore the text after the last newline, for example. GNU utilities have a policy of behaving decently with non-text files, and so do most other modern utilities, but you may still encounter odd behavior with files that are missing a final newline¹.
With GNU diff, if one of the files being compared ends with a newline but not the other, it is careful to note that fact. Since diff is line-oriented, it can't indicate this by storing a newline for one of the files but not for the others — the newlines are necessary to indicate where each line in the diff file starts and ends. So diff uses this special text
\ No newline at end of file
to differentiate a file that didn't end in a newline from a file that did.By the way, in a C context, a source file similarly consists of a series of lines. More precisely, a translation unit is viewed in an implementation-defined as a series of lines, each of which must end with a newline character (n1256 §5.1.1.1). On unix systems, the mapping is straightforward. On DOS and Windows, each CR LF sequence (
\r\n
) is mapped to a newline (\n
; this is what always happens when reading a file opened as text on these OSes). There are a few OSes out there which don't have a newline character, but instead have fixed- or variable-sized records; on these systems, the mapping from files to C source introduces a\n
at the end of each record. While this isn't directly relevant to unix, it does mean that if you copy a C source file that's missing its final newline to a system with record-based text files, then copy it back, you'll either end up with the incomplete last line truncated in the initial conversion, or an extra newline tacked onto it during the reverse conversion.¹ Example: the output of GNU sort always ends with a newline. So if the file
foo
is missing its final newline, you'll find thatsort foo | wc -c
reports one more character thancat foo | wc -c
.