1"""ndiff [-q] file1 file2 2 or 3ndiff (-r1 | -r2) < ndiff_output > file1_or_file2 4 5Print a human-friendly file difference report to stdout. Both inter- 6and intra-line differences are noted. In the second form, recreate file1 7(-r1) or file2 (-r2) on stdout, from an ndiff report on stdin. 8 9In the first form, if -q ("quiet") is not specified, the first two lines 10of output are 11 12-: file1 13+: file2 14 15Each remaining line begins with a two-letter code: 16 17 "- " line unique to file1 18 "+ " line unique to file2 19 " " line common to both files 20 "? " line not present in either input file 21 22Lines beginning with "? " attempt to guide the eye to intraline 23differences, and were not present in either input file. These lines can be 24confusing if the source files contain tab characters. 25 26The first file can be recovered by retaining only lines that begin with 27" " or "- ", and deleting those 2-character prefixes; use ndiff with -r1. 28 29The second file can be recovered similarly, but by retaining only " " and 30"+ " lines; use ndiff with -r2; or, on Unix, the second file can be 31recovered by piping the output through 32 33 sed -n '/^[+ ] /s/^..//p' 34""" 35 36__version__ = 1, 7, 0 37 38import difflib, sys 39 40def fail(msg): 41 out = sys.stderr.write 42 out(msg + "\n\n") 43 out(__doc__) 44 return 0 45 46# open a file & return the file object; gripe and return 0 if it 47# couldn't be opened 48def fopen(fname): 49 try: 50 return open(fname) 51 except IOError as detail: 52 return fail("couldn't open " + fname + ": " + str(detail)) 53 54# open two files & spray the diff to stdout; return false iff a problem 55def fcompare(f1name, f2name): 56 f1 = fopen(f1name) 57 f2 = fopen(f2name) 58 if not f1 or not f2: 59 return 0 60 61 a = f1.readlines(); f1.close() 62 b = f2.readlines(); f2.close() 63 for line in difflib.ndiff(a, b): 64 print(line, end=' ') 65 66 return 1 67 68# crack args (sys.argv[1:] is normal) & compare; 69# return false iff a problem 70 71def main(args): 72 import getopt 73 try: 74 opts, args = getopt.getopt(args, "qr:") 75 except getopt.error as detail: 76 return fail(str(detail)) 77 noisy = 1 78 qseen = rseen = 0 79 for opt, val in opts: 80 if opt == "-q": 81 qseen = 1 82 noisy = 0 83 elif opt == "-r": 84 rseen = 1 85 whichfile = val 86 if qseen and rseen: 87 return fail("can't specify both -q and -r") 88 if rseen: 89 if args: 90 return fail("no args allowed with -r option") 91 if whichfile in ("1", "2"): 92 restore(whichfile) 93 return 1 94 return fail("-r value must be 1 or 2") 95 if len(args) != 2: 96 return fail("need 2 filename args") 97 f1name, f2name = args 98 if noisy: 99 print('-:', f1name) 100 print('+:', f2name) 101 return fcompare(f1name, f2name) 102 103# read ndiff output from stdin, and print file1 (which=='1') or 104# file2 (which=='2') to stdout 105 106def restore(which): 107 restored = difflib.restore(sys.stdin.readlines(), which) 108 sys.stdout.writelines(restored) 109 110if __name__ == '__main__': 111 main(sys.argv[1:]) 112