Super User is a question and answer site for computer enthusiasts and power users. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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
up vote 11 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 \
     && echo '** are same' || echo '** are different'
** 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) \
     && echo '** are same' || echo '** are different'
** 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) \
>       && echo '** are same' || echo '** are different'
> 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

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

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

The answer is simple.
The message about the missing newline is not in the output stream of diff but in the error stream. So bend it to nirvana and you are done for good

diff -rqEeB fileA fileB 2> /dev/null
share|improve this answer
diff returns a value != 0 if it finds differences and I want to check that value. Redirecting to /dev/null does not make diff forget about that difference, so the value returned is != 0, which I do not want. I want diff to consider two files equal if the only difference is the last newline – jeckyll2hide May 21 '15 at 7:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .