Take the 2-minute tour ×

I have two big trees, which I want to compare. Some of the files in the tree differ just because one has newline at the end, and the other file lacks this newline. I want to ignore this fact. I have tried calling diff like this:

diff --ignore-all-space -r <dir1> <dir2>

And this is working. My problem is that it also ignores other differences (space-related), which may be important.

In summary: I just want to ignore the newline at EOF. Is this possible with diff?

share|improve this question
add comment

3 Answers

up vote 8 down vote accepted

You basically need to compare two files, conditionally ignoring the trailing byte. There isn't a 'diff' option to do this -- but there are a number of ways it could be done (e.g, hex diff comes to mind as well.)

To use 'diff', you basically have to modify the files that are missing the newline at the end of a file, and then compare. You could create a temporary directory with the modified files, or with a little bit of scripting it could be done in memory. (As to which is preferred depends on preference, file size, number of files...)

For example, the following will modify a file's contents (use sed -i to modify in-place, this just prints to stdout) to add a newline if one is missing (or leave the file unchanged if there already is a newline):

sed -e '$a\'  file1.txt

And just to review 'diff' syntax (returning true means they are the same, false means different):

$ diff a/file1.txt   b/file1.txt  \
      && echo '** are same' || echo '** are different'
< eof
> eof
\ No newline at end of file
** are different

Verify that only whitespace is different:

$ diff --ignore-all-space  a/file1.txt   b/file1.txt \
** are same

In bash, we can use 'sed' to manipulate the file contents as it is passed to 'diff' (original files left unchanged):

$ diff <(sed -e '$a\' a/file1.txt) <(sed -e '$a\' b/file1.txt) \
** are same

Now all you have to do is emulate diff -r to recursively compare directories. If comparing directories a and b, then for all files in a (e.g., a/dir1/dir2/file.txt) derive path to file in b (e.g., b/dir1/dir2/file.txt) and compare:

$ for f in $( find a -type f  )
> do
>    diff <(sed -e '$a\' $f) <(sed -e '$a\' b/${f#*/})
> done

A slightly more verbose version:

$ for f in $( find a -type f  )
> do
>   f1=$f
>   f2=b/${f#*/}
>   echo "compare: $f1 $f2"
>   diff <(sed -e '$a\' $f1) <(sed -e '$a\' $f2) \
> done && echo '** all are same' || echo '** all are different'
compare: a/file1.txt b/file1.txt
** are same
compare: a/file2.txt b/file2.txt
** are same
** all are same
share|improve this answer
add comment

Pipe the output of diff to a grep command that drops the message you don't want to see.

share|improve this answer
not good. diff -r exists with result != 0 if I do not add --ignore-all-space. To be clear: I want diff to ignore newlines at EOF, and only at EOF. And I want it to report a result which matches this criteria. That is, if files in the tree differ only on the newline at EOF, that must not be considered a difference, and thus diff must return 0. –  jeckyll2hide Feb 11 '12 at 12:10
add comment

Just thought of a different approach, too, that will work for larger files (and still doesn't copy or modify the original files). You would still have to emulate the recursive directory traversal (and there are a number of ways to do that), but this example doesn't use 'sed', but rather just compares two files, excluding the last byte, using cmp, e.g.,

$ cmp  a/file1.txt  b/file1.txt  && echo '** are same' || echo '** are different'
cmp: EOF on b/file1.txt
** are different

$ du -b a/file1.txt  b/file1.txt 
13  a/file1.txt
12  b/file1.txt

$ cmp  -n 12 a/file1.txt  b/file1.txt  && echo '** are same' || echo '** are different'
** are same

Still loop over all files in the directory, and for two files a/file.txt and b/file.txt, calculate the larger file size, and subtract one, then do a binary diff (cmp) using this number of bytes (also in bash):

(( bytes = $(du -b a/file.txt  b/file.txt  | sort -nr | head -1  | cut -f1) - 1 ))
cmp -n $bytes a/file.txt b/file.txt

Looping over the files would be the same as in the other answer using sed and diff.

share|improve this answer
add comment

Your Answer


