How to get the difference (only additions) between two files in linux
Asked Answered
K

8

119

I have two files A1 and A2 (unsorted). A1 is previous version of A2 and some lines have been added to A2. How can I get the new lines that are added to A2?

Note: I just want the new lines added and dont want the lines which were in A1 but deleted in A2. When i do diff A1 A2, I get the additions as well as deletions but I want only additions.

Please suggest a way to do this.

Kirghiz answered 13/3, 2013 at 12:4 Comment(1)
are all added lines in A2 new for the file? I mean no duplicates with existing lines?Punctuation
P
84

diff and then grep for the edit type you want.

diff -u A1 A2 | grep -E "^\+"
Phantasmal answered 13/3, 2013 at 12:7 Comment(5)
This will leave you with + at the beginning of the lineSelfabsorption
You can remove those with sed: diff -u A1 A2 | grep '^\+' | sed -E 's/^\+//'Pendulous
@AmauryD your edit gets rid of the first +++ A2 line, but leave a + sign at the beginning of every line, which is what the comment and sed command above are about.Footgear
You can combine the grep and sed in one command: diff -u A1 A2 | sed -n '/^+[^+]/ s/^+//p'Footgear
Drawback: this solution leaves lines which reference line numbers, e.g. @@ -31,6 +630,8 @@Ignace
J
128

Most of the below is copied directly from @TomOnTime's serverfault answer here. At the bottom is an attempt that works on unsorted files, but the command sorts the files before giving the diff so in many cases it will not be what is desired. For well-formatted diffs of unsorted files, you might find the other answers more useful (thanks to @Fritz for pointing this out):

Show lines that only exist in file a: (i.e. what was deleted from a)

comm -23 a b

Show lines that only exist in file b: (i.e. what was added to b)

comm -13 a b

Show lines that only exist in one file or the other: (but not both)

comm -3 a b | sed 's/^\t//'

(Warning: If file a has lines that start with TAB, it (the first TAB) will be removed from the output.)

NOTE: Both files need to be sorted for "comm" to work properly. If they aren't already sorted, you should sort them:

sort <a >a.sorted
sort <b >b.sorted
comm -12 a.sorted b.sorted

If the files are extremely long, this may be quite a burden as it requires an extra copy and therefore twice as much disk space.

Edit: note that the command can be written more concisely using process substitution (thanks to @phk for the comment):

comm -12 <(sort < a) <(sort < b)
Jerriejerrilee answered 26/2, 2016 at 5:11 Comment(6)
Since we are talking about bash here the last command can be simplified to comm -12 <(sort < a) <(sort < b) using process substitution.Av
You sir, are my hero.Mccaleb
Why does an answer that works only on sorted files have so many upvotes, despite the question clearly stating that the two files are unsorted?Stryker
This answer contains the command (see the last one in particular) that works if the files are not sorted. I think it makes sense to give the unsorted commands first since they are easier to understand, and to build up to the commands that do not assume sorted.Jerriejerrilee
@scottkosty: Thanks for your response. Sorry, I didn't communicate my point clearly in my previous comment. My issue with this approach (in contrast to the diff-based answers) is that the order of the lines is jumbled up by sort, and in many cases, order matters. Imagine comparing two source code files to see which function was added. Only, you sort the source code alphabetically before... Sorting code does not make sense. This is why I think the other answers are better. I think you should at least mention this caveat somewhere in your answer.Stryker
@Stryker Ah, that makes sense. I indeed did not even think about that. I agree, and edited in the caveat at the top. Thanks for clarifying.Jerriejerrilee
P
84

diff and then grep for the edit type you want.

diff -u A1 A2 | grep -E "^\+"
Phantasmal answered 13/3, 2013 at 12:7 Comment(5)
This will leave you with + at the beginning of the lineSelfabsorption
You can remove those with sed: diff -u A1 A2 | grep '^\+' | sed -E 's/^\+//'Pendulous
@AmauryD your edit gets rid of the first +++ A2 line, but leave a + sign at the beginning of every line, which is what the comment and sed command above are about.Footgear
You can combine the grep and sed in one command: diff -u A1 A2 | sed -n '/^+[^+]/ s/^+//p'Footgear
Drawback: this solution leaves lines which reference line numbers, e.g. @@ -31,6 +630,8 @@Ignace
C
75

You can try this

diff --changed-group-format='%>' --unchanged-group-format='' A1 A2

The options are documented in man diff:

       --GTYPE-group-format=GFMT
              format GTYPE input groups with GFMT

and:

       LTYPE is 'old', 'new', or 'unchanged'.
              GTYPE is LTYPE or 'changed'.

and:

              GFMT (only) may contain:

       %<     lines from FILE1

       %>     lines from FILE2

       [...]
Conclusive answered 13/3, 2013 at 12:16 Comment(6)
can you please explain these options, i couldnt get them from the man pageKirghiz
See this link for more gnu line group formatsConclusive
The '' after --unchanged-group-format='' looks like a single ", which won't work. Maybe change the '' to "" lest someone types your answer in with a single ".Pseudohermaphrodite
This is a much better answer than the selected answer, btw. Gives you exactly what you want, rather than the output littered with + symbols and an unnecessary meta line.Pseudohermaphrodite
For me, this also displays lines that were changed, not just completely new lines.Arboreal
This is the best answer,Spotty
T
13

A similar approach to https://mcmap.net/q/186310/-how-to-get-the-difference-only-additions-between-two-files-in-linux but hopefully more understandable and easy to tweak:

diff \
  --new-line-format="%L" \
  --old-line-format="" \
  --unchanged-line-format="" \
  A1 A2
Tentacle answered 6/8, 2018 at 16:52 Comment(0)
C
8

You can type:

grep -v -f A1 A2
Chemesh answered 13/3, 2013 at 12:11 Comment(3)
Assume file A1 contains one line x, and file A2 contains one line x and the other line xx. This command outputs nothing since both lines in A2 contains x.Phantasmal
grep's -x (--line-regexp) can be used to ensure the entire line is matched. So if A1 contains x and A2 contains xx, a match will not be found.Quadrat
You probably also need to use the option -F or --fixed-strings. Otherwise grep will be interpreting A1 as regular expressions. So if A1 contains the line .*, it will match everything. So the entire command would be: grep -vxF -f A1 A2Lujan
K
8
git diff path/file.css | grep -E "^\+" | grep -v '+++ b/' | cut -c 2-
  • grep -E "^\+" is from previous accepted answer, it is incomplete because leaves non-source stuff
  • grep -v '+++ b' removes non-source line with file name of later version
  • cut -c 2- removes column of + signs, also may use sed 's/^\+//'

comm or sdiff were not an option because of git.

Karinekariotta answered 21/11, 2016 at 10:57 Comment(1)
Best answer ! This return exactly the lines that have been added and nothing more. This should be the accepted answer I thinkAggie
S
7

The simple method is to use :

sdiff A1 A2

Another method is to use comm, as you can see in Comparing two unsorted lists in linux, listing the unique in the second file

Streeter answered 13/3, 2013 at 12:9 Comment(0)
G
0

You can filter only the lines that need to be added to fileA to become equal to fileB. We include the first line that indicates the line number to apply the change.

diff $fileA $fileB | grep '^>' -B 1
Gattis answered 14/7, 2023 at 3:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.