Another diff in Tcl, by HE, 2012-01-09, saves resources by holding neither the file contents nor the result in the memory.
This implementation is efficient in both computation and memory usage, but instead of using the longest-common-subsequence algorithm, it progresses through the subsequences matching the earliest possible lines, and may therefore present a more fragemented and less concise view of the differences in a file.
# Syntax: # filediff <file1> <file2> <cmdEqual> <cmdAdd> <cmdDel> # Description: # Compare two files line by line directly from the local filesystem and provide the result with the stated procs. # with: # <file1> = One of the files to compare. # <file2> = The other file to compare. # <cmdEqual> = Procedure to call if two lines are equal. Default is 'cmdEqual'. # <cmdAdd> = Procedure to call if an addtional row is found in <file2>. Default is 'cmdAdd'. # <cmdDel> = Procedure to call if an row is found which not exist in <file2>. Default is 'cmdDel'. # # Create your own procedures to output the result if you need anything different than simply write to stdout with puts proc filediff {file1 file2 {cmdEqual {cmdEqual}} {cmdAdd {cmdAdd}} {cmdDel {cmdDel}}} { set sourcefid1 [open $file1 r] set sourcefid2 [open $file2 r] set found 1 while {![eof $sourcefid1] && ![eof $sourcefid2]} { set lastmark [tell $sourcefid2] ;# Position in <file2> before reading the next line gets $sourcefid1 line1 gets $sourcefid2 line2 if {$line1 eq $line2} { $cmdEqual $line1 continue } # Lines with only whitespace are also equal if {[regexp -- {^\s*$} $line1] && [regexp -- {^\s*$} $line2]} { $cmdEqual {} continue } # From here both lines are unequal set state 0 while {[regexp -- {^\s*$} $line1]} { # If unequal then directly state empty lines in <file1> as deleted. $cmdDel $line1 if {![eof $sourcefid1]} { gets $sourcefid1 line1 if {$line1 eq $line2} { $cmdEqual $line1 set state 1 break } } else { break } } if {$state} { continue } # Remember position in <file2> and look forward set mark2 [tell $sourcefid2] set mark2a $lastmark set found 0 while {![eof $sourcefid2]} { gets $sourcefid2 line2 if {$line1 ne $line2} { set mark2a $mark2 set mark2 [tell $sourcefid2] } else { # Found a matching line. Everything up to the line before are new lines seek $sourcefid2 $lastmark while {[tell $sourcefid2] <= $mark2a} { gets $sourcefid2 line2 $cmdAdd $line2 } gets $sourcefid2 line2 $cmdEqual $line2 set found 1 break } } if {!$found} { # No matching line found in <file2>. Line must be deleted $cmdDel $line1 seek $sourcefid2 $lastmark } } # Output the rest of <file1> as deleted while {![eof $sourcefid1]} { gets $sourcefid1 line1 $cmdDel $line1 } # Output the rest of <file2> as added while {![eof $sourcefid2]} { gets $sourcefid2 line2 $cmdAdd $line2 } close $sourcefid2 close $sourcefid1 return } proc cmdEqual {txt} { global n puts "=$txt";update } proc cmdAdd {txt} { global n puts "+$txt";update } proc cmdDel {txt} { global n puts "-$txt";update }