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?
6 Answers
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.txtAnd 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'
2c2
< eof
---
> eof
\ No newline at end of file
** are differentVerify that only whitespace is different:
$ diff --ignore-all-space a/file1.txt b/file1.txt \ && echo '** are same' || echo '** are different'
** are sameIn 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 sameNow 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#*/})
> doneA 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 3 I solved the problem by adding a newline to each of the files and ignoring blank lines in the diff (option -B). This solutions may not be suitable for your use case but it might help others:
echo >> $FILE1
echo >> $FILE2
diff -B $FILE1 FILE2 2 Pipe the output of diff to a grep command that drops the message you don't want to see.
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 sameStill 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.txtLooping over the files would be the same as in the other answer using sed and diff.
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 1 There is a flag in diff commnad: --strip-trailing-cr that do exactly what you asked for