如何区分文件而忽略注释(以 # 开头的行)?

Lek*_*eyn 63 diff regular-expression

我有两个配置文件,一个来自包管理器的原始配置文件和一个由我自己修改的自定义配置文件。我添加了一些注释来描述行为。

如何diff在配置文件上运行,跳过注释?注释行定义为:

  • 可选的前导空格(制表符和空格)
  • 哈希符号(# )
  • 任何其他字符

跳过第一个要求的(最简单的)正则表达式是#.*. 我尝试了GNU diff 3.0的--ignore-matching-lines=RE( -I RE) 选项,但我无法让它与那个 RE 一起工作。我也尝试过.*#.*.*\#.*但没有运气。从字面上看,将 ( Port 631)行作为RE不匹配任何内容,将 RE 放在斜杠之间也无济于事。

正如“差异”工具中所建议的那样,正则表达式似乎缺乏?,我试过grep -G

grep -G '#.*' file
Run Code Online (Sandbox Code Playgroud)

这似乎与评论相符,但不适用于diff -I '#.*' file1 file2.

那么,这个选项应该如何使用呢?如何diff跳过某些行(在我的情况下,评论)?请不要建议greping 文件和比较临时文件。

Lek*_*eyn 66

根据 Gilles 的说法,该-I选项仅在该集合中除匹配-I. 在我测试它之前我没有完全理解它。

考试

我的测试涉及三个文件:
File test1

    text
Run Code Online (Sandbox Code Playgroud)

文件test2

    text
    #comment
Run Code Online (Sandbox Code Playgroud)

文件test3

    changed text
    #comment
Run Code Online (Sandbox Code Playgroud)

命令:

$ # comparing files with comment-only changes
$ diff -u -I '#.*' test{1,2}
$ # comparing files with both comment and regular changes
$ diff -u -I '#.*' test{2,3}
--- test2       2011-07-20 16:38:59.717701430 +0200
+++ test3       2011-07-20 16:39:10.187701435 +0200
@@ -1,2 +1,2 @@
-text
+changed text
 #comment
Run Code Online (Sandbox Code Playgroud)

另一种方式

由于到目前为止还没有解释如何-I正确使用该选项的答案,我将提供一个适用于 bash shell 的替代方法:

diff -u -B <(grep -vE '^\s*(#|$)' test1)  <(grep -vE '^\s*(#|$)' test2)
Run Code Online (Sandbox Code Playgroud)
  • diff -u - 统一差异
    • -B - 忽略空行
  • <(command)- 一个称为进程替换的 bash 特性,它为命令打开一个文件描述符,这消除了对临时文件的需要
  • grep - 打印行(不)匹配模式的命令
    • -v - 显示不匹配的行
    • E - 使用扩展的正则表达式
    • '^\s*(#|$)' - 匹配注释和空行的正则表达式
      • ^ - 匹配一行的开头
      • \s* - 匹配空格(制表符和空格),如果有的话
      • (#|$) 匹配一个哈希标记,或者匹配一行的结尾


ken*_*orb 8

尝试:

diff -b -I '^#' -I '^ #' file1 file2
Run Code Online (Sandbox Code Playgroud)

请注意,正则表达式必须匹配两个文件中的相应行,并且它匹配大块中的每个更改行才能工作,否则它仍然会显示差异。

使用单引号来保护模式免受外壳扩展并转义正则表达式保留的字符(例如括号)。

我们可以在diffutils手册中阅读:

但是,-I如果大块中的每个更改行(每次插入和每次删除)都与正则表达式匹配,则仅忽略包含正则表达式的行的插入或删除。

换句话说,对于每个不可忽略的更改,diff打印其附近的完整更改集,包括可忽略的更改。您可以使用多个-I选项为要忽略的行指定多个正则表达式。diff尝试将每一行与每个正则表达式匹配,从给定的最后一个开始。

armel here也很好地解释了这种行为。

相关:如何执行忽略所有评论的差异?


小智 5

在网上搜索后,我找到了一种类似于 Lekensteyn 的方法。

但我想使用diff输出作为输入patch,并grep -v更改格式,所以我不能。

这是一个改进,也许:

diff -u -B <(sed 's/^[[:blank:]]*#.*$/ /' file1)  <(sed 's/^[[:blank:]]*#.*$/ /' file2)
Run Code Online (Sandbox Code Playgroud)

它并不完美,但行号保存在补丁文件中。

但是,如果添加了新行而不是注释行,则注释将导致修补时大块失败:

File test1:
  text
  #comment
  other text
File test2:
  text
  new line here
  #comment changed
  other text changed
Run Code Online (Sandbox Code Playgroud)

使用我们的命令测试该数据:

$ echo -e "#!/usr/bin/sed -f\ns/^[[:blank:]]*#.*$/ /" > outcom.sed
$ echo "diff -u -B <(./outcom.sed \$1)  <(./outcom.sed \$2)" > mydiff.sh
$ chmod +x mydiff.sh outcom.sed
$ ./mydiff.sh file1 file2 > file.dif
$ cat file.dif
--- /dev/fd/63  2014-08-23 10:05:08.000000000 +0200
+++ /dev/fd/62  2014-08-23 10:05:08.000000000 +0200
@@ -1,2 +1,3 @@
 text
+new line
  
-other text
+other text changed
Run Code Online (Sandbox Code Playgroud)

/dev/fd/62 & /dev/fd/63 是进程替换产生的文件。"+new line" 和 "-other text" 之间的那一行是默认的空格字符,我们在 sed 表达式中定义它来替换注释。

应用该补丁会给我们带来错误:

$ patch -p0 file1 < file.dif 
patching file file1
Hunk #1 FAILED at 1.
1 out of 1 hunk FAILED -- saving rejects to file file1.rej
Run Code Online (Sandbox Code Playgroud)

一个解决方案是不使用统一的 diff 格式;所以,没有-u

$ echo "diff -B <(./outcom.sed \$1)  <(./outcom.sed \$2)" > mydiff.sh
$ ./mydiff.sh file1 file2 > file.dif
$ cat file.dif
1a2
> new line
3c4
< other text
---
> other text changed
$ patch -p0 file1 < file.dif 
patching file file1
$ cat file1
text
new line
#comment
other text changed
Run Code Online (Sandbox Code Playgroud)

现在补丁文件可以工作了(但不能保证任何更复杂的东西)。