it-swarm-ru.tech

Как различать файлы, игнорируя комментарии (строки начинающиеся с #)?

У меня есть два файла конфигурации, оригинальный от менеджера пакетов и один измененный мной. Я добавил несколько комментариев для описания поведения.

Как я могу запустить diff для файлов конфигурации, пропуская комментарии? Комментируемая строка определяется как:

  • необязательный начальный пробел (табуляции и пробелы)
  • знак хеша (#)
  • ничего другого персонажа

(Самое простое) регулярное выражение, пропускающее первое требование, будет #.*. Я попробовал --ignore-matching-lines=RE (-I RE) опция GNU diff 3.0, но я не смог заставить ее работать с этим RE. Я также попробовал .*#.* а также .*\#.* без удачи. Буквально ставить строку (Port 631) поскольку RE ничего не соответствует, и при этом это не помогает помещать RE между слешами.

Как указано в похоже, что в "diff" отсутствует регулярное выражение? , я пробовал grep -G:

grep -G '#.*' file

Это похоже на комментарии, но не работает для diff -I '#.*' file1 file2.

Итак, как использовать эту опцию? Как я могу заставить diff пропускать определенные строки (в моем случае, комментарии)? Пожалуйста, не предлагайте greping файл и сравнивать временные файлы.

56
Lekensteyn

По словам Жиля, опция _-I_ игнорирует только строку, если ничего внутри этого набора не совпадает, за исключением совпадения _-I_. Я не получил его полностью, пока не проверил.

Тест

В моем тесте участвуют три файла:
Файл _test1_ :

_    text
_

Файл _test2_ :

_    text
    #comment
_

Файл _test3_ :

_    changed text
    #comment
_

Команды:

_$ # 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
_

Альтернативный способ

Поскольку пока нет ответа, объясняющего, как правильно использовать параметр _-I_, я предоставлю альтернативу, которая работает в оболочках bash:

_diff -u -B <(grep -vE '^\s*(#|$)' test1)  <(grep -vE '^\s*(#|$)' test2)
_
  • _diff -u_ - унифицированный дифференциал
    • _-B_ - игнорировать пустые строки
  • <(command) - функция bash, называемая подстановка процесса , которая открывает дескриптор файла для команды, что устраняет необходимость во временном файле
  • grep - команда для печати строк (не), соответствующих шаблону
    • _-v_ - показывать несовпадающие строки
    • E - использовать расширенные регулярные выражения
    • '^\s*(#|$)' - регулярное выражение, соответствующее комментариям и пустым строкам
      • _^_ - соответствует началу строки
      • _\s*_ - совпадать с пробелами (табуляцией и пробелами), если есть
      • _(#|$)_ соответствует хеш-метке или, альтернативно, концу строки
54
Lekensteyn

Пытаться:

diff -b -I '^#' -I '^ #' file1 file2

Обратите внимание, что регулярное выражение должно совпадать с соответствующей строкой в ​​обоих файлах, и оно соответствует каждой измененной строке в блоке, чтобы работать, иначе оно все равно покажет разницу.

Используйте одинарные кавычки, чтобы защитить шаблон от расширения Shell и экранировать символы, зарезервированные для регулярных выражений (например, скобки).

Мы можем прочитать в diffutils руководстве:

Однако, -I игнорирует только вставку или удаление строк, которые содержат регулярное выражение, если каждая измененная строка в блоке (каждая вставка и каждое удаление) соответствует регулярному выражению.

Другими словами, для каждого невосполнимого изменения diff печатает полный набор изменений в его окрестности, включая игнорируемые. Вы можете указать более одного регулярного выражения для игнорируемых строк, используя более одного -I вариант. diff пытается сопоставить каждую строку с каждым регулярным выражением, начиная с последнего заданного.

Это поведение также хорошо объясняется armel здесь .

Связанный: Как я могу выполнить diff, который игнорирует все комментарии?

7
kenorb

После поиска по сети, альтернативный способ Лекенштейна - лучший, который я нашел.

Но я хочу использовать вывод diff как патч ... и есть проблема, потому что номер строки записывается из-за "grep -v".

Поэтому я намерен улучшить эту командную строку:

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

Это не идеально, но номер строки хранится в файле патча.

Однако, если вместо строки комментария будет добавлена ​​новая строка ... комментарий будет производить Hunk FAILED при исправлении, как мы видим ниже.

File test1:
  text
  #comment
  other text
File test2:
  text
  new line here
  #comment changed
  other text changed

проверить сейчас нашу команду

$ 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

/ dev/fd/62 &/dev/fd/63 - это файл, созданный путем подстановки процесса. Строка между "+ новая строка" и "-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

Решение состоит в том, чтобы не использовать унифицированный формат 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

теперь рабочий файл патча (без гарантии результата в очень сложном процессе сравнения).

3
syjust

Я обычно игнорирую этот беспорядок:

  • Генерация некомментированных версий с использованием grep -v "^#" | cat -s и ​​различие тех или ...
  • С помощью vim -d чтобы посмотреть файлы. Подсветка синтаксиса позволяет сделать различия между комментариями и комментариями, не являющимися комментариями, совершенно очевидными. Подсветка diff различий в строке, чтобы вы могли сразу увидеть, какие значения или части значений были изменены, делает это моим любимым.
1
Caleb

Вот что я использую, чтобы удалить все закомментированные строки, даже те, которые начинаются с табуляции или пробела, и пустые:

egrep -v "^$|^[[:space:]]*#" /path/to/file

или вы можете сделать

sed -e '/^#.*/d' -e 's/#.*//g' | cat -s
0
Philomath