I have a huge number of source files that are all lacking a newline at the end.
How do I automatically add a newline to the end of each of them?
Some may alr
find -type f | while read f; do [[ `tail -c1 "$f"` ]] && echo >> "$f"; done
I'm using find
instead of for f in *
as it is recursive and the question was about "huge number of source files".
I'm using while read
instead of find -exec
or xargs
for performance reasons, it saves spawning shell process every time.
I'm taking advantage of the fact that backtick operator is returning output of command "with any trailing newlines deleted" man bash
, so for properly terminated files backtick will be empty and echo will be skipped.
The find | read
couple will fail on filenames that contain newlines, but it's easy to fix if required:
find -type f -print0 | while read -d $'\0' f; do [[ `tail -c1 "$f"` ]] && echo >> "$f"; done
A simple fix for files that are "missing" newline at end of file is simply sed; the following fixes the file "in-place" (using the "-i" option):
find . -type f -exec sed -i -e '$a\' {} \; -print
Explanation: find all files (-type f
), run sed
, change the files in-place (-i
), given the following (-e
) script/expression, which matches the end of the file ($
), and perform the "append" action (a\
), but don't actually specify any text to append (nothing after the \
) which is going to add a newline to the end of the file, but only if it's missing. Prints all files found (fixed or not), which is probably unnecessary.
Main caveat is that sed
features vary across platforms, so -i
and -e
may or may not be supported / the same; e.g. older Unix, or MacOS oddities may require slightly different syntax.
Converted Norman's answer to a split one-liner for convenience.
for i in * ; do echo $i; \
if diff /dev/null "$i" | tail -1 | \
grep '^\\ No newline' > /dev/null; then echo >> "$i"; \
fi; done
Replace * with whatever file pattern you want, eg *.c
And another to just tell you which files are broken:
for i in * ; do \
if diff /dev/null "$i" | tail -1 | \
grep '^\\ No newline' > /dev/null; then echo $i; \
fi; done
Below is my bash script solution. It first checks that the file is a text file. Then, if it's a text file, it uses tail and od (octal dump) to see if the last character is the newline character. If it isn't, then it appends a newline using echo:
item="$1"
if file "$item" | egrep '\btext\b' > /dev/null
then
if ! tail -c 1 "$item" | od -b -A n | egrep '\b012\b' > /dev/null
then
echo "(appending final newline to ${item})"
echo >> "$item"
fi
fi
Due To command localization Tim and Norman answer Shall be improved using 'LANG=C' prefix to have a chance to match 'No newline' pattern with every system having any regional parameters
This ensures an ending empty line to every file put on the command line of this script :
#!/bin/sh -f
for i in $* ; do echo $i; \
if LANG=C diff /dev/null "$i" | tail -1 | \
grep '^\\ No newline' > /dev/null; then echo >> "$i"; \
fi; done
And this script detects files lacking of it :
#!/bin/sh -f
for i in $* ; do \
if LANG=C diff /dev/null "$i" | tail -1 | \
grep '^\\ No newline' > /dev/null; then echo $i; \
fi; done
After finding the tool do this job with no luck. I decide to write my own
This is my python script to do that job
It only append (\r\n) to file not contains (\n) at the end of file
https://github.com/tranhuanltv/append_newline
Usage: append_newline.py .c ./projects ./result_dir
Make Pull Requests if you want to