31

Over at Can you modify text files when committing to subversion? Grant suggested that I block commits instead.

However I don't know how to check a file ends with a newline. How can you detect that the file ends with a newline?

Community
  • 1
  • 1
grom
  • 15,842
  • 19
  • 64
  • 67

10 Answers10

29

Here is a useful bash function:

function file_ends_with_newline() {
    [[ $(tail -c1 "$1" | wc -l) -gt 0 ]]
}

You can use it like:

if ! file_ends_with_newline myfile.txt
then
    echo "" >> myfile.txt
fi
# continue with other stuff that assumes myfile.txt ends with a newline
Steve Kehlet
  • 6,156
  • 5
  • 39
  • 40
  • 2
    This should be the accepted answer; it works correctly for empty files, files ending in a null byte, and regular files. The pipe to `wc -l` is kind of unattractive but it does exactly the right thing -- it counts the number of newlines in the last byte of the file. If it's not one, the file does not end with a newline. – tripleee Dec 08 '17 at 08:18
23

@Konrad: tail does not return an empty line. I made a file that has some text that doesn't end in newline and a file that does. Here is the output from tail:

$ cat test_no_newline.txt
this file doesn't end in newline$ 

$ cat test_with_newline.txt
this file ends in newline
$

Though I found that tail has get last byte option. So I modified your script to:

#!/bin/sh
c=`tail -c 1 $1`
if [ "$c" != "" ]; then
    echo "no newline"
fi
wjandrea
  • 28,235
  • 9
  • 60
  • 81
grom
  • 15,842
  • 19
  • 64
  • 67
  • 4
    This answer is not optimal because I have no clue which Konrad you are referring to. – oberlies Dec 23 '14 at 15:40
  • 1
    `[ "$c" != "" ]` is `false` for completely empty files which is contrary to what I'd expect. – Jani Uusitalo Apr 01 '17 at 14:04
  • 2
    This also fails if the last byte is a null byte. You cannot distinguish between "empty" and "ends with a newline" in a command substitution, which trims the final newline. – tripleee Dec 08 '17 at 08:16
17

Or even simpler:

#!/bin/sh
test "$(tail -c 1 "$1")" && echo "no newline at eof: '$1'"

But if you want a more robust check:

test "$(tail -c 1 "$1" | wc -l)" -eq 0 && echo "no newline at eof: '$1'"
FelipeC
  • 9,123
  • 4
  • 44
  • 38
4

You could use something like this as your pre-commit script:

#! /usr/bin/perl

while (<>) {
    $last = $_;
}

if (! ($last =~ m/\n$/)) {
    print STDERR "File doesn't end with \\n!\n";
    exit 1;
}
bstark
  • 511
  • 3
  • 6
4

Using only bash:

x=`tail -n 1 your_textfile`
if [ "$x" == "" ]; then echo "empty line"; fi

(Take care to copy the whitespaces correctly!)

@grom:

tail does not return an empty line

Damn. My test file didn't end on \n but on \n\n. Apparently vim can't create files that don't end on \n (?). Anyway, as long as the “get last byte” option works, all's well.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
3

Worked for me:

tail -n 1 /path/to/newline_at_end.txt | wc --lines
# according to "man wc" : --lines - print the newline counts

So wc counts number of newline chars, which is good in our case. The oneliner prints either 0 or 1 according to presence of newline at the end of the file.

KarolDepka
  • 8,318
  • 10
  • 45
  • 58
2

A complete Bash solution with only tail command, that also deal correctly with empty files.

#!/bin/bash
# Return 0 if file $1 exists and ending by end of line character,
# else return 1
[[ -s "$1" && -z "$(tail -c 1 "$1")" ]]
  • -s "$1" checks if the file is not empty
  • -z "$(tail -c 1 "$1")" checks if its last (existing) character is end of line character
  • the all [[...]] conditional expression is returned

You can also defined this Bash function to use it in your scripts.

# Return 0 if file $1 exists and ending by end of line character,
# else return 1
check_ending_eol() {
    [[ -s "$1" && -z "$(tail -c 1 "$1")" ]]
}
Olivier Pirson
  • 737
  • 1
  • 5
  • 24
0

The read command can not read a line without newline.

if tail -c 1 "$1" | read -r line; then
  echo "newline"
fi

Another answer.

if [ $(tail -c 1 "$1" | od -An -b) = 012 ]; then
  echo "newline"
fi
Koichi Nakashima
  • 799
  • 8
  • 10
0

I'm coming up with a correction to my own answer.

Below should work in all cases with no failures:

nl=$(printf '\012')
nls=$(wc -l "${target_file}")
lastlinecount=${nls%% *}
lastlinecount=$((lastlinecount+1))
lastline=$(sed ${lastlinecount}' !d' "${target_file}")
if [ "${lastline}" = "${nl}" ]; then
    echo "${target_file} ends with a new line!"
else
    echo "${target_file} does NOT end with a new line!"
fi
Nicolae Iotu
  • 399
  • 3
  • 7
0

You can get the last character of the file using tail -c 1.

   my_file="/path/to/my/file"

   if [[ $(tail -c 1 "$my_file") != "" ]]; then
      echo "File doesn't end with a new line: $my_file"
   fi
thanos.a
  • 2,246
  • 3
  • 33
  • 29