比较两个文件和两个文件的输出差异(包括行号和内容)

Posted

技术标签:

【中文标题】比较两个文件和两个文件的输出差异(包括行号和内容)【英文标题】:Compare two files & output differences (including Line Number and content) from both files 【发布时间】:2021-09-28 17:35:50 【问题描述】:

我试图在另一个文件或标准输出中获取两个文件、行号和内容的差异。我尝试了以下操作,但无法获得所需的确切输出。请看下文。

文件内容:

文件1:

Col1,Col2,Col3
Text1,text1,text1
Text2,text2,Rubbish

文件2:

Col1,Col2,Col3
Text1,text1,text1
Text2,text2,text2
Text3,text3,text3

我已经尝试了以下代码,它没有提供确切的所需输出,因为它只显示了第一个文件中的差异,而不是 file2 中的额外行。

sort file1 file2 | uniq | awk 'FNR==NR a[$1]; next  !($1 in a) print FNR": "$0' file2 file1

输出

3: Text2,text2,Rubbish

期望的输出

3: Text2,text2,Rubbish (File1)
3: Text2,text2,text2 (File2)
4: Text3,text3,text3 (File2)

由于输出原因,我不希望使用 diff/sdiff/comm,因为我无法添加行号并并排组织数据以便于阅读。普通文件会超过 1000 行,因此 diff/sdiff 实用程序变得更难阅读。

【问题讨论】:

【参考方案1】:

使用您显示的示例,请尝试遵循awk 代码。用 GNU awk 编写和测试。

awk '
BEGIN  OFS=": " 
FNR==1 next     
FNR==NR
  arr[$0]=FNR
  next

!($0 in arr)
  print FNR,$0" ("FILENAME")"
  next


  arr1[$0]

END
  for(key in arr)
    if(!(key in arr1))
      print arr[key],key" ("ARGV[1]")"
    
  

' file1 file2

说明:为上述添加详细说明。

awk '                                   ##Starting awk program from here.
BEGIN  OFS=": "                       ##Setting OFS to colon space in BEGIN section of this program.
FNR==1 next                           ##Skipping if there is FNR==1 for both the files.
FNR==NR                                ##Checking condition if FNR==NR then do following.
  arr[$0]=FNR                           ##Creating arr with index of current line has FNR as value.
  next                                  ##Will skip all further statements from here.

!($0 in arr)                           ##If current line is NOT in arr(to get lines which are in file2 but not in file1)
  print FNR,$0" ("FILENAME")"           ##Printing as per OP request number with file name, line.
  next                                  ##Will skip all further statements from here.


  arr1[$0]                              ##Creating arr1 which has index as current line in it.

END                                    ##Starting END section of this program from here.
  for(key in arr)                      ##Traversing through arr here.
    if(!(key in arr1))                 ##If key is NOT present in arr1.
      print arr[key],key" ("ARGV[1]")"   ##Printing values of arr and first file name, basically getting lines which are present in file1 and NOT in file2.
    
  

' file1 file2                           ##Mentioning Input_file names here.

【讨论】:

非常感谢您的帮助和详细解释。我喜欢这两种解决方案,因此很难选择 - 但由于您的解决方案非常详细、清晰且仅使用 awk - 我接受了您的回答。【参考方案2】:

您可以使用 GNU diff + awk 获得所需的输出:

funkydiff()
   # configure diff to output:
   #    - 1 or 2 as flag character to identify file
   #    - space
   #    - line number formatted as integer
   #    - colon, space
   #    - the line content including terminating newline
   diff -d \
       --old-line-format='1 %dn: %L' \
       --new-line-format='2 %dn: %L' \
       --unchanged-line-format='' \
       -- "$1" "$2" \
   | awk '
      # p starts off null
      # initialise filename lookup table
      !p  f[1]=a; f[2]=b 

      
         # discard first 2 characters of input line
         # append bracketed filename if flag character changed
         # print the result
         print substr($0,3) ($1!=p?" ("f[$1]")":"")

         # update p ready for next line
         p=$1
      
   ' a="$1" b="$2"


funkydiff File1 File2

GNU diff 完成了大部分艰苦的工作。

awk 检查(并删除)diff 在每行开头添加的文件名标识符前缀,并在必要时在括号中附加适当的文件名。


根据您修改后(更简单)的要求,即在每一行打印文件名,而不仅仅是每组的第一行,awk 可以简化:

funkydiff2()
   diff -d \
      --old-line-format='1 %dn: %L' \
      --new-line-format='2 %dn: %L' \
      --unchanged-line-format='' \
      -- "$1" "$2" \
   | awk '
      !p  f[1]=a; f[2]=b 

      # always append bracketed filename
      # update p as a side-effect (just for brevity)
       print substr($0,3) " (" f[p=$1] ")" 
   ' a="$1" b="$2"


事实上,如果文件名不包含特殊的% 字符,则根本不需要 awk:

funkydiff3()
    case "$1$2" in
        *%*)
            echo 1>&2 "ERROR: funky filename. Aborting"
            return 1
            ;;
    esac

    # now $1 and $2 cannot contain the % metacharacter
    # %c'\012' produces newline
    # alternatively you could embed literal newlines
    diff -d \
        --old-line-format="%dn: %l ($1)%c'\012'" \
        --new-line-format="%dn: %l ($2)%c'\012'" \
        --unchanged-line-format="" \
        -- "$1" "$2"

因为文件名直接嵌入到传递给diff 的格式字符串中,如果它们包含任何%,格式字符串要么会更改为意外的内容、格式错误,要么会导致文件名在输出时被破坏。

GNU diff 的手册页包含它允许在格式字符串中使用的 % 序列的详细信息。

【讨论】:

谢谢 - 我修改了所需的输出,因为我错过了为 file2 的 line3 添加文件名。请问您能否进一步分解您的 awk 代码?我删除了 awk 部分并得到了类似的输出。 awk 的新手,但试图更多地使用它。 1 3: Text2,text2,Rubbish 2 3: Text2,text2,text2 2 4: Text3,text3,text3 非常感谢您提供的帮助。我不知道 diff 有那么强大,并且可以在没有 awk 的情况下做我需要的事情。 gnu.org/software/diffutils/manual/html_node/Line-Formats.html 使用此站点了解有关行格式的更多信息。再次感谢您的支持。我可以确认您的解决方案也有效。

以上是关于比较两个文件和两个文件的输出差异(包括行号和内容)的主要内容,如果未能解决你的问题,请参考以下文章

Beyond Compare如何比较文件内容的差异 如何对比文件内容的差异

python 有没有一个模块可以比较两个文本文件内容差异的?而且可以只输出差异的部分?

Git diff 比较两个版本文件之间的差异

比较两个文件的内容(差异)

请教LINUX两个文件内容比较的写法

python比较两个文件的差异